Last active
January 4, 2021 08:34
-
-
Save leehosung/f784d9efc71dce12855739647dd98877 to your computer and use it in GitHub Desktop.
PyCon Korea 2017 - dodo fighter
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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