Skip to content

Instantly share code, notes, and snippets.

@5pecia1
Forked from segfault87/dodo_fighter_agent.py
Created August 18, 2018 12:22
Show Gist options
  • Save 5pecia1/98e2502f89d6980b63ed6c8fa60b37b2 to your computer and use it in GitHub Desktop.
Save 5pecia1/98e2502f89d6980b63ed6c8fa60b37b2 to your computer and use it in GitHub Desktop.
도도 파이터(https://pycon-2018-dodo-fighter.spoqa.com) 에이전트 예제 코드
import json
import sys
# 도도 파이터에 참가하기 위해서는 에이전트를 만들어서 제출해 주셔야 합니다.
# 에이전트는 사용자가 작성하는 인공지능 코드로서, 주어지는 현재 게임 상태를 바탕으로
# 어떤 액션을 취할지를 결정하는 역할을 합니다.
#
# 액션 설명
# - idle - 아무것도 하지 않습니다.
# - forward - 앞으로 움직입니다. 상대가 바로 앞에 있을 경우 더 움직이지 않습니다.
# - backward - 뒤로 움직입니다. 처음 시작지점에서 세칸 이상 뒤로 갈 수 없습니다.
# - punch - 상단을 공격합니다.
# - kick - 하단을 공격합니다.
# - crouch - 상단 공격을 피합니다.
# - jump - 하단 공격을 피합니다.
# - guard - 공격을 방어합니다. 상하단 모두 방어할 수 있지만 약간의 데미지를 입습니다.
#
# 상태 설명
# - distance - 상대방과 나와의 거리. 0일 경우에만 공격이 가능합니다.
# - time_left - 남은 시간
# - health - 나의 체력
# - opponent_health - 상대의 체력
# - opponent_action - 지난 턴에서 상대의 액션
# - given_damage - 직전 액션에서 내가 상대방에게 가한 데미지
# - taken_damage - 직전 액션에서 상대방이 나에게 가한 데미지
# - match_records - 지금까지의 경기 기록. 리스트 형식입니다.
# 예를 들어 [None, True, False]인 경우, 첫번째 경기는 무승부,
# 두번째 경기는 당신이, 세번째 경기는 상대방이 이겼다는 뜻입니다.
#
# 주의사항
# - 같은 액션을 계속 반복하지 마세요. 공격력이 하락되는 페널티가 있습니다.
# - 상대의 공격을 회피하거나 막는데 성공하면 다음 공격에서 공격력 보너스가 있습니다.
# - 한 턴 내에서는 이동과 방어 동작이 공격 동작보다 우선합니다.
# 즉, P1이 공격을 하고 P2가 이동한다면 P2가 이동하는 액션을 우선 평가합니다.
# - 사용할 수 있는 모듈은 random, json, sys, math로 한정되어 있습니다.
# - 스크립트 실행 시간이 3초를 넘어가면 탈락 처리됩니다.
def action(what):
if what not in ('idle', 'forward', 'backward', 'punch', 'kick',
'crouch', 'jump', 'guard'):
raise ValueError(f'Unknown action type: {what}')
sys.stdout.write(what + '\n')
sys.stdout.flush()
def read_status():
data = sys.stdin.readline()
while data:
yield json.loads(data)
data = sys.stdin.readline()
for status in read_status():
distance = status['distance']
time_left = status['time_left']
health = status['health']
opponent_health = status['opponent_health']
opponent_action = status['opponent_action']
given_damage = status['given_damage']
taken_damage = status['taken_damage']
match_records = status['match_records']
# 아래 코드를 수정해 주세요!
# 주의할 점은, 한 루프 내에는 반드시 한 번의 action()만 호출되어야 합니다.
# 호출되지 않으면 스크립트가 강제종료되며, 여러번 호출되면 큐가 쌓여서
# 의도하지 않은 결과가 나옵니다.
action('punch')
# -*- coding: utf-8 -*-
import argparse
import enum
import json
import random
import subprocess
import sys
import time
import typing
# 이 코드는 도도 파이터의 에이전트 코드를 로컬에서 평가하기 위한 스크립트입니다.
# 실제 도도 파이터의 데미지 계산 공식과는 차이가 있는 점을 유념해 주십시오.
#
# 사용 방법: python judge.py [1P] [2P]
# 이 때, 에이전트 스크립트는 쉘에서 직접 실행 가능한 상태여야 합니다.
# * 스크립트의 첫 줄에 `#!/usr/bin/env python3` 가 있어야 합니다.
# * 스크립트는 실행 권한이 있어야 합니다.
ROUND_TIME = 30
HIT_POINT_RANGE = (8, 16)
HIT_POINT_GUARD_RANGE = (4, 8)
class Action(enum.Enum):
idle = 'idle'
forward = 'forward'
backward = 'backward'
punch = 'punch'
kick = 'kick'
crouch = 'crouch'
jump = 'jump'
guard = 'guard'
class Player:
def __init__(self, prochandle: subprocess.Popen, position: int):
self.prochandle = prochandle
self.health = 100
self.last_action = None
self.last_inflicted_damage = 0
self.position = position
def distance(self, opponent):
return abs(opponent.position - self.position)
def communicate(self, opponent, time_left: int) -> Action:
payload = {
'distance': self.distance(opponent),
'time_left': time_left,
'health': self.health,
'opponent_health': opponent.health,
'opponent_action': str(opponent.last_action),
'given_damage': self.last_inflicted_damage,
'taken_damage': opponent.last_inflicted_damage,
'match_records': [],
}
self.prochandle.stdin.write(json.dumps(payload).encode('utf-8'))
self.prochandle.stdin.write(b'\n')
self.prochandle.stdin.flush()
act = self.prochandle.stdout.readline().decode('utf-8').strip()
self.last_action = Action(act)
return self.last_action
def inflict_damage(self, opponent, damage: int):
opponent.health = max(0, opponent.health - damage)
self.last_inflicted_damage = damage
def __repr__(self) -> str:
return f'<Player position={self.position} health={self.health} action={self.last_action} damage={self.last_inflicted_damage}>' # noqa
def log(time: int, player: Player, action: Action, message: str):
print(f'{time} [{player.health}] {message}')
def fight_loop(p1, p2):
time_left = ROUND_TIME
while time_left >= 0:
try:
p1a = p1.communicate(p2, time_left)
except:
print('Invalid communication from p1. Assuming that p2 has won.')
raise
return p2
try:
p2a = p2.communicate(p1, time_left)
except:
print('Invalid communication from p2. Assuming that p1 has won.')
raise
return p1
p1.last_inflicted_damage = 0
p2.last_inflicted_damage = 0
p1_idle = True
p2_idle = True
if p1a is Action.forward:
if p2.position > p1.position:
p1.position += 1
log(time_left, p1, p1a, 'P1은 앞으로 움직였다!')
else:
log(time_left, p1, p1a, 'P1은 앞으로 움직이려 했지만 더 갈 수 없다!')
p1_idle = False
elif p1a is Action.backward:
if p1.position >= -2:
p1.position -= 1
log(time_left, p1, p1a, 'P1은 뒤로 움직였다!')
p1_idle = False
if p2a is Action.forward:
if p2.position > p1.position:
p2.position -= 1
log(time_left, p2, p2a, 'P2는 앞으로 움직였다!')
else:
log(time_left, p2, p2a, 'P2는 앞으로 움직이려 했지만 더 갈 수 없다!')
p2_idle = False
elif p2a is Action.backward:
if p2.position <= 5:
p2.position += 1
log(time_left, p2, p2a, 'P2는 뒤로 움직였다!')
p2_idle = False
if p1a is Action.punch:
if p1.distance(p2) > 0:
log(time_left, p1, p1a, 'P1의 펀치! 하지만 닿지 않는다!')
elif p2a is Action.crouch:
log(time_left, p2, p2a, 'P2는 숙였다!')
log(time_left, p1, p1a, 'P1의 펀치! 하지만 P2는 숙여서 피했다!')
elif p2a is Action.guard:
log(time_left, p2, p2a, 'P2는 가드를 올렸다!')
log(time_left, p1, p1a, 'P1의 펀치! 약간의 데미지를 주었다!')
p1.inflict_damage(p2, random.randrange(*HIT_POINT_GUARD_RANGE))
else:
log(time_left, p1, p1a, 'P1의 펀치! 아프다!')
p1.inflict_damage(p2, random.randrange(*HIT_POINT_RANGE))
p1_idle = False
elif p1a is Action.kick:
if p1.distance(p2) > 0:
log(time_left, p1, p1a, 'P1의 발차기! 하지만 닿지 않는다!')
elif p2a is Action.jump:
log(time_left, p2, p2a, 'P2의 점프!')
log(time_left, p1, p1a, 'P1의 발차기! 하지만 P2는 점프해서 피했다!')
elif p2a is Action.guard:
log(time_left, p2, p2a, 'P2는 가드를 올렸다!')
log(time_left, p1, p1a, 'P1의 발차기! 약간의 데미지를 주었다!')
p1.inflict_damage(p2, random.randrange(*HIT_POINT_GUARD_RANGE))
else:
log(time_left, p1, p1a, 'P1의 발차기! 아프다!')
p1.inflict_damage(p2, random.randrange(*HIT_POINT_RANGE))
p1_idle = False
if p2.health <= 0:
print('KO. P1 승리!')
return p1
if p2a is Action.punch:
if p2.distance(p1) > 0:
log(time_left, p2, p2a, 'P2의 펀치! 하지만 닿지 않는다!')
elif p1a is Action.crouch:
log(time_left, p1, p1a, 'P1은 숙였다!')
log(time_left, p2, p2a, 'P2의 펀치! 하지만 P1은 숙여서 피했다!')
elif p1a is Action.guard:
log(time_left, p2, p2a, 'P1은 가드를 올렸다!')
log(time_left, p1, p1a, 'P2의 펀치! 약간의 데미지를 주었다!')
p2.inflict_damage(p1, random.randrange(*HIT_POINT_GUARD_RANGE))
else:
log(time_left, p2, p2a, 'P1의 펀치! 아프다!')
p2.inflict_damage(p1, random.randrange(*HIT_POINT_RANGE))
p2_idle = False
elif p2a is Action.kick:
if p2.distance(p1) > 0:
log(time_left, p2, p2a, 'P2의 발차기! 하지만 닿지 않는다!')
elif p1a is Action.jump:
log(time_left, p1, p1a, 'P1의 점프!')
log(time_left, p2, p2a, 'P2의 발차기! 하지만 P1은 점프해서 피했다!')
elif p1a is Action.guard:
log(time_left, p1, p1a, 'P1는 가드를 올렸다!')
log(time_left, p2, p2a, 'P2의 발차기! 약간의 데미지를 주었다!')
p2.inflict_damage(p1, random.randrange(*HIT_POINT_GUARD_RANGE))
else:
log(time_left, p2, p2a, 'P2의 발차기! 아프다!')
p2.inflict_damage(p1, random.randrange(*HIT_POINT_RANGE))
p2_idle = False
if p1.health <= 0:
print('KO. P2 승리!')
return p2
if p1_idle:
log(time_left, p1, p1a, '아무 일도 없었다.')
if p2_idle:
log(time_left, p2, p2a, '아무 일도 없었다.')
time_left -= 1
if p1.health == p2.health:
print('무승부!')
return None
elif p1.health > p2.health:
print('타임 오버. P1 승리!')
return p1
else:
print('타임 오버. P2 승리!')
return p2
def main():
parser = argparse.ArgumentParser()
parser.add_argument('p1', type=str)
parser.add_argument('p2', type=str)
args = parser.parse_args()
p1 = subprocess.Popen([args.p1],
stdout=subprocess.PIPE,
stdin=subprocess.PIPE)
p2 = subprocess.Popen([args.p2],
stdout=subprocess.PIPE,
stdin=subprocess.PIPE)
try:
winner = fight_loop(Player(p1, 0), Player(p2, 3))
except KeyboardInterrupt:
p1.terminate()
p2.terminate()
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment