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
노드그레이 텔레그램 공지방
노드그레이 텔레그램 채팅방
'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 |
댓글