Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
PyCon Korea 2017 - dodo fighter
import json
import sys
import random
# 도도 파이터에 참가하기 위해서는 에이전트를 만들어서 제출해 주셔야 합니다.
# 에이전트는 사용자가 작성하는 인공지능 코드로서, 주어지는 현재 게임 상태를 바탕으로
# 어떤 액션을 취할지를 결정하는 역할을 합니다.
#
# 액션 설명
# - 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초를 넘어가면 탈락 처리됩니다.
last_action = 'idle'
attack_actions = ['punch', 'kick']
defence_actions = ['crouch', 'jump', 'guard']
available_backsteps = 3
accumulated_given_damage = 0
accumulated_taken_damage = 0
def action(what):
global last_action
if what not in ('idle', 'forward', 'backward', 'punch', 'kick',
'crouch', 'jump', 'guard'):
raise ValueError(f'Unknown action type: {what}')
last_action = 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()
def attack():
"""공격"""
action(random.choice(attack_actions))
def defence():
"""방어와 회피
방어의 최선은 맞지 않는 것입니다. 뒤로 도망갈 수 있으면 도망갑니다.
더 이상 도망갈 곳이 없으면 랜덤으로 피하거나 막습니다.
"""
global available_backsteps
if available_backsteps > 0:
available_backsteps -= 1
action('backward')
else:
action(random.choice(defence_actions))
def success_defence(taken_damage, opponent_action):
"""방어에 성공 했는가?
거리가 0 일때, 다음 3가지 경우는 방어에 성공한 것입니다.
- 방어(guard)를 선택했는데 데미지를 입은 경우
- 상단회피(crouch)를 선택했는데 상대방이 상단공격(punch)을 한 경우
- 하단회피(jump)를 선택했는데 상대방이 하단공격(kick)을 한 경우
방어에 성공하면 공격력 보너스를 받습니다. 공격!
:param taken_damage: 내가 입은 데미지
:param opponent_action: 상대방이 선택한 동작
:return:
bool: 방어에 성공 했으면 True, 방어에 실패 했으면 False
"""
return (taken_damage > 0 and last_action == 'guard') or \
(last_action == 'crouch' and opponent_action == 'punch') or \
(last_action == 'jump' and opponent_action == 'kick')
def fail_attack(given_damage, opponent_action):
"""공격에 실패했는가? 즉, 상대방이 방어,회피에 성공 했는가?
거리가 0일 떄, 다음 3가지 경우는 상대방이 방어에 성공한 것입니다.
- 공격을 선택했는데 상대편이 방어(guard)를 선택한 경우
- 공격을 선택했는데 상대편이 데미지를 입지 않은 경우, 즉 상,하단을 맞춰서 피한 경우
상대방이 공격력 보너스를 받습니다. 도망가거나 방어를 합시다.
:param given_damage: 상대방이 맞은 데미지
:param opponent_action: 상대방이 선택한 동작
:return:
bool: 공격에 실패 했으면 True, 공격에 성공 했으면 False
"""
return last_action in attack_actions and (given_damage == 0 or opponent_action == 'guard')
def do_random_action(health, opponent_health):
"""랜덤 행동을 합니다.
자리를 이동하지 않고 공격성에 따라서 랜덤 행동을 합니다. 지고 있을 수록 적극적으로 공격을 해서 역전을 노려야 합니다.
공격성은 "상대방의 체력/(상대방의 체력 + 나의 체력)" 으로 정의합니다. 양쪽의 체력이 같을 때에는 공격과 방어를 선택할 확률이 같습니다.
:param health: 나의 체력
:param opponent_health: 상대방의 체력
"""
aggressivity = opponent_health * 1.0 / (opponent_health + health)
random_action = random.choices([attack, defence], weights=[aggressivity, 1 - aggressivity])[0]
random_action()
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']
accumulated_given_damage += given_damage
accumulated_taken_damage += taken_damage
# 거리가 2칸이면 다가가도 맞지 않습니다. 한 걸음 다가갑니다. 나중에 도망갈 여유를 만들어 둡니다.
if distance >= 2:
action('forward')
available_backsteps += 1
# 거리가 1칸이면 다가가면 맞습니다. 상대방이 다가올지 모르니 공격을 합시다.
elif distance == 1:
# 서로가 싸운적이 있으면 다가가지 않습니다. 한번도 싸우지 않은 경우에는 가만히 있는 더미 에이전트를 이기기 위해서 5초를 남기고 다가갑니다.
if accumulated_given_damage == 0 and accumulated_taken_damage == 0 and time_left < 5:
action('forward')
# 상대방이 다가오지 않으면 허공에 계속 주먹질, 발길질을 하고 있을 가능성이 높습니다.
# 같은 동작을 연속으로 하면 패널티가 있습니다. 주먹질, 발길질을 번갈아 가면서 합시다.
elif last_action == 'punch':
action('kick')
elif last_action == 'kick':
action('punch')
# 아무 공격이나 합시다.
else:
attack()
# 거리가 0칸 입니다.
else:
# 방어에 성공했다면 공격력 보너스가 생깁니다. 공격합시다.
if success_defence(taken_damage, opponent_action):
attack()
# 공격에 실패했다면 상대방이 공격력 보너스가 생깁니다. 도망가거나 막읍시다.
elif fail_attack(given_damage, opponent_action):
defence()
# 양쪽다 보너스가 없는 상황이라면 랜덤행동을 합니다.
else:
do_random_action(health, opponent_health)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.