mirror of
https://github.com/peter-tanner/Algorithms-Agents-and-Artificial-Intelligence-project-final.git
synced 2024-11-30 15:10:17 +08:00
199 lines
6.1 KiB
Python
199 lines
6.1 KiB
Python
|
|
||
|
from copy import deepcopy
|
||
|
from datetime import datetime
|
||
|
import math
|
||
|
import pathlib
|
||
|
import pickle
|
||
|
from typing import List
|
||
|
from etc.custom_float import Uncertainty
|
||
|
from etc.gamestate import GameState, Winner
|
||
|
from etc.messages import Message, Opinion
|
||
|
from agents.blue import BlueAgent
|
||
|
from agents.red import RedAgent
|
||
|
|
||
|
import play_config as cfg
|
||
|
|
||
|
|
||
|
def rand_rounds(gs: GameState) -> GameState:
|
||
|
blue_gamestate = deepcopy(gs)
|
||
|
gs.blue_agent.smart_influence(gs)
|
||
|
red_gamestate = deepcopy(gs)
|
||
|
gs.red_agent.smart_influence(gs)
|
||
|
# gs.draw_green_network()
|
||
|
# gs.green_round()
|
||
|
# gs.draw_green_network()
|
||
|
# spy = bool(gs.rand.getrandbits(1))
|
||
|
# spy = False
|
||
|
gs.green_round()
|
||
|
gs.draw_green_network()
|
||
|
gs.red_agent.update_short_term_mem(red_gamestate, gs)
|
||
|
gs.blue_agent.update_short_term_mem(blue_gamestate, gs)
|
||
|
gs.iteration += 1
|
||
|
return gs
|
||
|
|
||
|
|
||
|
def main():
|
||
|
sel_parameters = input_opts("""Would you like to select parameters?
|
||
|
[y] -> Yes
|
||
|
[n] -> No
|
||
|
> """, ['y', 'n'])
|
||
|
if sel_parameters == 'y':
|
||
|
select_parameters()
|
||
|
|
||
|
cfg.ENABLE_SNAPSHOTS = ('y' == input_opts("""Would you like to enable snapshots?
|
||
|
[y] -> Yes
|
||
|
[n] -> No
|
||
|
> """, ['y', 'n']))
|
||
|
|
||
|
if cfg.ENABLE_SNAPSHOTS:
|
||
|
snapshot_path = pathlib.Path(cfg.GAMESTATE_PARAMETER_SNAPSHOT_OUTPUT_DIR)
|
||
|
snapshot_path.mkdir(parents=True, exist_ok=True)
|
||
|
|
||
|
cfg.ENABLE_GRAPHICS = ('y' == input_opts("""Enable graphics?
|
||
|
Note that this option is currently exporting to an image file for compatibility on headless operating systems (WSL)
|
||
|
[y] -> Graphics
|
||
|
[n] -> No graphics
|
||
|
> """, ['y', 'n']))
|
||
|
|
||
|
while True:
|
||
|
print("Starting new game")
|
||
|
state_buffer: List[GameState] = []
|
||
|
# gs: GameState = GameState.short_init((100, 0.05, 0.5), (10, 0.5))
|
||
|
gs: GameState = GameState(
|
||
|
green_agents=(cfg.N_GREEN_AGENT, cfg.P_GREEN_AGENT_CONNECTION, cfg.P_GREEN_AGENT_BLUE),
|
||
|
gray_agents=(cfg.N_GRAY_AGENT, cfg.P_GRAY_AGENT_FRIENDLY),
|
||
|
uncertainty_interval=cfg.INITIAL_UNCERTAINTY_RANGE,
|
||
|
seed=None, graphics=cfg.ENABLE_GRAPHICS)
|
||
|
|
||
|
player = input_opts("""Choose a team
|
||
|
[r] -> Red
|
||
|
[b] -> Blue
|
||
|
[n] -> None
|
||
|
> """, ['r', 'b', 'n'])
|
||
|
|
||
|
player: Opinion = {
|
||
|
'r': Opinion.RED,
|
||
|
'b': Opinion.BLUE,
|
||
|
'n': Opinion.UNDEFINED
|
||
|
}[player]
|
||
|
|
||
|
state_buffer.append(deepcopy(gs))
|
||
|
|
||
|
while gs.winner == Winner.NO_WINNER:
|
||
|
print(gs.print_gamestate_pretty())
|
||
|
|
||
|
print("Blue turn")
|
||
|
blue_gamestate = deepcopy(gs)
|
||
|
if player == Opinion.BLUE:
|
||
|
option = select_potency(BlueAgent.choices())
|
||
|
gs.blue_agent.influence(gs, option)
|
||
|
else:
|
||
|
gs.blue_agent.dumb_influence(gs)
|
||
|
|
||
|
print("Red turn")
|
||
|
red_gamestate = deepcopy(gs)
|
||
|
if player == Opinion.RED:
|
||
|
option = select_potency(RedAgent.choices())
|
||
|
gs.red_agent.influence(gs, option)
|
||
|
else:
|
||
|
gs.red_agent.dumb_influence(gs)
|
||
|
|
||
|
print("Green turn")
|
||
|
gs.green_round()
|
||
|
gs.draw_green_network()
|
||
|
|
||
|
gs.red_agent.update_short_term_mem(red_gamestate, gs)
|
||
|
gs.blue_agent.update_short_term_mem(blue_gamestate, gs)
|
||
|
gs.iteration += 1
|
||
|
gs.update_winner()
|
||
|
state_buffer.append(deepcopy(gs))
|
||
|
gs.close()
|
||
|
|
||
|
print(f"""
|
||
|
Game over
|
||
|
Round {gs.iteration}, reason {gs.winner}
|
||
|
Winner {Winner.winner_opinion(gs.winner)}""")
|
||
|
if player != Opinion.UNDEFINED:
|
||
|
print("YOU WIN 🎉" if Winner.winner_opinion(gs.winner) == player else "YOU LOSE 💀")
|
||
|
print(gs.print_gamestate_pretty())
|
||
|
input("Press enter to continue...")
|
||
|
|
||
|
# Save snapshot of game
|
||
|
if cfg.ENABLE_SNAPSHOTS:
|
||
|
with snapshot_path.joinpath(f"game_snapshot_{datetime.now().isoformat().replace(':','_')}.bin").open("wb") as f:
|
||
|
pickle.dump(state_buffer, f, protocol=pickle.HIGHEST_PROTOCOL)
|
||
|
|
||
|
|
||
|
def select_potency(messages: List[Message]) -> Message:
|
||
|
prompt_str = "Choose a potency\n"
|
||
|
for i in range(0, len(messages)):
|
||
|
prompt_str += f"[{i}] -> {str(messages[i])}\n"
|
||
|
return messages[input_p_int(prompt_str, 0, len(messages))]
|
||
|
|
||
|
|
||
|
def select_parameters() -> None:
|
||
|
# Override config defaults
|
||
|
|
||
|
print("\nBlue agent\n---")
|
||
|
cfg.INITIAL_BLUE_ENERGY = input_float("Blue agent initial energy?\n> ")
|
||
|
print("\nBounds of uniform uncertainty distribution at start of game for green agents\n---")
|
||
|
cfg.INITIAL_UNCERTAINTY_RANGE = (input_uncertainty("Lower\n> "), input_uncertainty("Upper\n> "))
|
||
|
|
||
|
print("\nGreen agents\n---")
|
||
|
cfg.N_GREEN_AGENT = input_p_int("Number of green agents\n> ")
|
||
|
cfg.P_GREEN_AGENT_CONNECTION = input_probability("Probability of green agent connections [0,1]\n> ")
|
||
|
cfg.P_GREEN_AGENT_BLUE = input_p_int("Probability of green agents initialized to blue opinion\n> ")
|
||
|
|
||
|
print("\nGray agents\n---")
|
||
|
cfg.N_GRAY_AGENT = input_p_int("Number of gray agents\n> ")
|
||
|
cfg.P_GRAY_AGENT_FRIENDLY = input_probability("Probability of gray agents being blue\n> ")
|
||
|
|
||
|
|
||
|
def input_probability(prompt: str) -> float:
|
||
|
while True:
|
||
|
value = input_T(prompt, float)
|
||
|
if value > 0.0 and value < 1.0:
|
||
|
return value
|
||
|
else:
|
||
|
print("Invalid input")
|
||
|
|
||
|
|
||
|
def input_p_int(prompt: str, l_=0, u_=math.inf) -> int:
|
||
|
while True:
|
||
|
value = input_T(prompt, int)
|
||
|
if value >= l_ and value < u_:
|
||
|
return value
|
||
|
else:
|
||
|
print("Invalid input")
|
||
|
|
||
|
|
||
|
def input_float(prompt: str) -> float:
|
||
|
return input_T(prompt, float)
|
||
|
|
||
|
|
||
|
def input_uncertainty(prompt: str) -> Uncertainty:
|
||
|
return input_T(prompt, Uncertainty)
|
||
|
|
||
|
|
||
|
def input_T(prompt: str, type):
|
||
|
num: type = None
|
||
|
while num is None:
|
||
|
try:
|
||
|
num = type(input(prompt))
|
||
|
except ValueError:
|
||
|
print("Invalid input")
|
||
|
return num
|
||
|
|
||
|
|
||
|
def input_opts(prompt: str, opts: List[str]) -> str:
|
||
|
option: str = ""
|
||
|
while option not in opts:
|
||
|
if len(option) > 0:
|
||
|
print("Invalid input")
|
||
|
option = input(prompt).strip().lower()
|
||
|
return option
|
||
|
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
main()
|