
AO Testnet Guide 4편
Bots and Games

1. Let's Play A Game

AO-EFFECT 게임이란
실시간으로 전 세계의 친구들이나 다른 플레이어와 경쟁 할 수 있는 게임이다.
규칙
- 각 플레이어의 체력은 100으로 시작하며 에너지는 0으로 시작한다.
- 40x40의 맵에서 시작한다.
- 에너지는 시간이 지남에 따라 100까지 보충된다.
- 맵을 탐색하고, 다른 플레이어를 찾아 공격해서 이겨야한다.
- 범위 내에 있을 때만 에너지를 사용하여 공격할 수 있다.
- 한 명의 플레이어만 남거나 할당된 시간이 만료되면 게임이 끝이난다.
2. 게임 Process ID 설정
// 게임 ID의 주소도 기존 9pe-GCPGdQ-WlCKBj1E7079s0Sl75DDOVgZwhA2RJYY에서 변경되었다. aos> Game = "3HSmhQ-lHaCQlOKtq5GDgbVQXQ6mWIp40uUASAG13Xk"
3. 게임 토큰 요청 및 게임 서버 등록
// 게임 서버 등록 aos> Send({ Target = Game, Action = "Register" }) // 토큰 요청 aos> Send({ Target = Game, Action = "RequestTokens" })
게임에 참가하려면 토큰이 필요한데 위 명령어를 통해 토큰을 요청할 수 있다.
4. 게임참가
aos> Send({ Target = Game, Action = "Transfer", Recipient = Game, Quantity = "1000" })
토큰을 받은후 입장료를 지불하여 게임에 참가한다.
몇 초만 기다리면 플레이어 결제 상태에 대한 실시간 업데이트가 화면에 표시된다.
2명이상의 플레이어가 참가한 후, 2분 후에 게임이 시작된다.
5. 게임 플레이
// 예시 aos> Send({ Target = Game, Action = "PlayerMove", Player = ao.id, Direction = "DownRight"}) // 방향종류, 위의 예시에서 Direction = "DownRight" 부분을 아래 원하는 종류로 변경하면된다. Up = {x = 0, y = -1}, Down = {x = 0, y = 1}, Left = {x = -1, y = 0}, Right = {x = 1, y = 0}, UpRight = {x = 1, y = -1}, UpLeft = {x = -1, y = -1}, DownRight = {x = 1, y = 1}, DownLeft = {x = -1, y = 1}
맵의 밖으로 이동하면 그 반대편으로 이동한다.
6. 공격
aos> Send({ Target = Game, Action = "PlayerAttack", Player = ao.id, AttackEnergy = "energy_integer"})
3x3 범위 내의 다른 플레이어를 공격하는데 축적된 에너지를 사용한다.
여기서부터 대충 한번 보고 바로 11번으로 넘어가도된다.
7. Announcement 전달 봇 만들기
// Ctrl + c 를 눌러 aos 밖으로 나온다 nano bot.lua // 생성 후 아래 코드 복사 붙여넣기 Handlers.add( "PrintAnnouncements", Handlers.utils.hasMatchingTag("Action", "Announcement"), function (msg) print(msg.Event .. ": " .. msg.Data) end )
Ctrl x, Y, 엔터

핸들러의 이름은 "PrintAnnouncements"
내장된 특수 유틸리티로 hasMatchingTags를 사용하며
announcement의 메세지를 받을 때
변환하여 보여준다.
8. 리로드 및 테스트
.load bot.lua

9. 게임의 진행상태를 체크 봇 만들기
// Ctrl + c 입력 후 aos 밖으로 나온다. nano bot.lua // HandleAnnouncements 핸들러 코드를 아래의 코드로 대체한다. Handlers.add( "HandleAnnouncements", Handlers.utils.hasMatchingTag("Action", "Announcement"), function (msg) ao.send({Target = Game, Action = "GetGameState"}) print(msg.Event .. ": " .. msg.Data) end )
Ctrl x, Y, 엔터

aos .load bot.lua
10. 전체적으로 수정하기
nano bot.lua //아래 코드로 전부 대체한다. LatestGameState = LatestGameState or nil Handlers.add( "HandleAnnouncements", Handlers.utils.hasMatchingTag("Action", "Announcement"), function (msg) ao.send({Target = Game, Action = "GetGameState"}) print(msg.Event .. ": " .. msg.Data) end ) Handlers.add( "UpdateGameState", Handlers.utils.hasMatchingTag("Action", "GameState"), function (msg) local json = require("json") LatestGameState = json.decode(msg.Data) ao.send({Target = ao.id, Action = "UpdatedGameState"}) print("Game state updated. Print \'LatestGameState\' for detailed view.") end )
Ctrl x, Y, 엔터
aos .load bot.lua
11. Strategic Decisions and 자동 응답
// Ctrl + c로 aos 밖으로 나온 후 nano bot.lua //아래 코드 전체로 대체 LatestGameState = LatestGameState or nil InAction = InAction or false Logs = Logs or {} colors = { red = "\27[31m", green = "\27[32m", blue = "\27[34m", reset = "\27[0m", gray = "\27[90m" } function addLog(msg, text) Logs[msg] = Logs[msg] or {} table.insert(Logs[msg], text) end function inRange(x1, y1, x2, y2, range) return math.abs(x1 - x2) <= range and math.abs(y1 - y2) <= range end function decideNextAction() local player = LatestGameState.Players[ao.id] local targetInRange = false for target, state in pairs(LatestGameState.Players) do if target ~= ao.id and inRange(player.x, player.y, state.x, state.y, 1) then targetInRange = true break end end if player.energy > 5 and targetInRange then print(colors.red .. "Player in range. Attacking." .. colors.reset) ao.send({Target = Game, Action = "PlayerAttack", Player = ao.id, AttackEnergy = tostring(player.energy)}) else print(colors.red .. "No player in range or insufficient energy. Moving randomly." .. colors.reset) local directionMap = {"Up", "Down", "Left", "Right", "UpRight", "UpLeft", "DownRight", "DownLeft"} local randomIndex = math.random(#directionMap) ao.send({Target = Game, Action = "PlayerMove", Player = ao.id, Direction = directionMap[randomIndex]}) end InAction = false end Handlers.add( "PrintAnnouncements", Handlers.utils.hasMatchingTag("Action", "Announcement"), function (msg) if msg.Event == "Started-Waiting-Period" then ao.send({Target = ao.id, Action = "AutoPay"}) elseif (msg.Event == "Tick" or msg.Event == "Started-Game") and not InAction then InAction = true ao.send({Target = Game, Action = "GetGameState"}) elseif InAction then print("Previous action still in progress. Skipping.") end print(colors.green .. msg.Event .. ": " .. msg.Data .. colors.reset) end ) Handlers.add( "GetGameStateOnTick", Handlers.utils.hasMatchingTag("Action", "Tick"), function () if not InAction then InAction = true print(colors.gray .. "Getting game state..." .. colors.reset) ao.send({Target = Game, Action = "GetGameState"}) else print("Previous action still in progress. Skipping.") end end ) Handlers.add( "AutoPay", Handlers.utils.hasMatchingTag("Action", "AutoPay"), function (msg) print("Auto-paying confirmation fees.") ao.send({ Target = Game, Action = "Transfer", Recipient = Game, Quantity = "1000"}) end ) Handlers.add( "UpdateGameState", Handlers.utils.hasMatchingTag("Action", "GameState"), function (msg) local json = require("json") LatestGameState = json.decode(msg.Data) ao.send({Target = ao.id, Action = "UpdatedGameState"}) print("Game state updated. Print \'LatestGameState\' for detailed view.") end ) Handlers.add( "decideNextAction", Handlers.utils.hasMatchingTag("Action", "UpdatedGameState"), function () if LatestGameState.GameMode ~= "Playing" then InAction = false return end print("Deciding next action.") decideNextAction() ao.send({Target = ao.id, Action = "Tick"}) end ) Handlers.add( "ReturnAttack", Handlers.utils.hasMatchingTag("Action", "Hit"), function (msg) if not InAction then InAction = true local playerEnergy = LatestGameState.Players[ao.id].energy if playerEnergy == undefined then print(colors.red .. "Unable to read energy." .. colors.reset) ao.send({Target = Game, Action = "Attack-Failed", Reason = "Unable to read energy."}) elseif playerEnergy == 0 then print(colors.red .. "Player has insufficient energy." .. colors.reset) ao.send({Target = Game, Action = "Attack-Failed", Reason = "Player has no energy."}) else print(colors.red .. "Returning attack." .. colors.reset) ao.send({Target = Game, Action = "PlayerAttack", Player = ao.id, AttackEnergy = tostring(playerEnergy)}) end InAction = false ao.send({Target = ao.id, Action = "Tick"}) else print("Previous action still in progress. Skipping.") end end )
Ctrl + x, Y, 엔터
aos .load bot.lua
12.Expanding the Arena
// Ctrl + c 입력 후 밖으로 나온다. nano ao-effect.lua // 아래 전체 복사 후 붙여넣기 Width = 40 Height = 40 Range = 1 MaxEnergy = 100 EnergyPerSec = 1 AverageMaxStrengthHitsToKill = 3 function playerInitState() return { x = math.random(0, Width), y = math.random(0, Height), health = 100, energy = 0 } end function onTick() if GameMode ~= "Playing" then return end if LastTick == undefined then LastTick = Now end local Elapsed = Now - LastTick if Elapsed >= 1000 then for player, state in pairs(Players) do local newEnergy = math.floor(math.min(MaxEnergy, state.energy + (Elapsed * EnergyPerSec // 2000))) state.energy = newEnergy end LastTick = Now end end function move(msg) local playerToMove = msg.From local direction = msg.Tags.Direction local directionMap = { Up = {x = 0, y = -1}, Down = {x = 0, y = 1}, Left = {x = -1, y = 0}, Right = {x = 1, y = 0}, UpRight = {x = 1, y = -1}, UpLeft = {x = -1, y = -1}, DownRight = {x = 1, y = 1}, DownLeft = {x = -1, y = 1} } if directionMap[direction] then local newX = Players[playerToMove].x + directionMap[direction].x local newY = Players[playerToMove].y + directionMap[direction].y Players[playerToMove].x = (newX - 1) % Width + 1 Players[playerToMove].y = (newY - 1) % Height + 1 announce("Player-Moved", playerToMove .. " moved to " .. Players[playerToMove].x .. "," .. Players[playerToMove].y .. ".") else ao.send({Target = playerToMove, Action = "Move-Failed", Reason = "Invalid direction."}) end onTick() end function attack(msg) local player = msg.From local attackEnergy = tonumber(msg.Tags.AttackEnergy) local x = Players[player].x local y = Players[player].y if Players[player].energy < attackEnergy then ao.send({Target = player, Action = "Attack-Failed", Reason = "Not enough energy."}) return end Players[player].energy = Players[player].energy - attackEnergy local damage = math.floor((math.random() * 2 * attackEnergy) * (1/AverageMaxStrengthHitsToKill)) announce("Attack", player .. " has launched a " .. damage .. " damage attack from " .. x .. "," .. y .. "!") for target, state in pairs(Players) do if target ~= player and inRange(x, y, state.x, state.y, Range) then local newHealth = state.health - damage if newHealth <= 0 then eliminatePlayer(target, player) else Players[target].health = newHealth ao.send({Target = target, Action = "Hit", Damage = tostring(damage), Health = tostring(newHealth)}) ao.send({Target = player, Action = "Successful-Hit", Recipient = target, Damage = tostring(damage), Health = tostring(newHealth)}) end end end end function inRange(x1, y1, x2, y2, range) return x2 >= (x1 - range) and x2 <= (x1 + range) and y2 >= (y1 - range) and y2 <= (y1 + range) end Handlers.add("PlayerMove", Handlers.utils.hasMatchingTag("Action", "PlayerMove"), move) Handlers.add("PlayerAttack", Handlers.utils.hasMatchingTag("Action", "PlayerAttack"), attack)
Ctrl + x, Y, 엔터
aos .load ao-effect.lua
귀찮지만 잘 따라와서 고생많았다.
여기까지 기본적인 Tutorial이 끝났으며
다음편에는 퀘스트를 진행하여 CRED를 얻을 것 이다.
스팸 댓글이 많아서 본 게시글의 댓글 확인을 안합니다.
궁금하신 점이 있다면 아래 채팅방으로 문의 부탁드립니다.
퍼가실 땐 출처 명시 부탁드립니다.
노드 대행 및 기타 문의
graychoi0920@gmail.com
노드그레이 텔레그램 공지방
노드그레이
그레이의 50가지 노드 그림자
t.me
노드그레이 텔레그램 채팅방
노드그레이 채팅방
공지방 : https://t.me/nodegray
t.me
'Blockchain > Testnet' 카테고리의 다른 글
Polymer 테스트넷 가이드 1편 (58) | 2024.03.12 |
---|---|
AO 테스트넷 퀘스트 가이드 (0) | 2024.03.05 |
AO 테스트넷 가이드 3편 (0) | 2024.03.05 |
AO 테스트넷 가이드 2편 (0) | 2024.03.05 |
AO 테스트넷 가이드 1편 (0) | 2024.03.04 |
댓글