diff --git a/main.py b/main.py new file mode 100644 index 0000000..c974682 --- /dev/null +++ b/main.py @@ -0,0 +1,299 @@ +from time import sleep +import requests +import numpy as np +import random +import argparse + +ozai_url = 'http://localhost:8000/api/' + + +# ozai classes (some are just storing json data because i am ) + +class Player: + def __init__(self, name, gamedata_player_json): + self.name = name + self.ready = gamedata_player_json['ready'] + self.points = gamedata_player_json['points'] + self.pattern_lines = gamedata_player_json['pattern_lines'] #'pattern_lines': [{'color': None, 'number': 0}, {'color': None, 'number': 0}, {'color': None, 'number': 0}, {'color': None, 'number': 0}, {'color': None, 'number': 0}], + self.wall = gamedata_player_json['wall'] # 'wall': [[False, False, False, False, False], [False, False, False, False, False], [False, False, False, False, False], [False, False, False, False, False], [False, False, False, False, False]] + self.floor = gamedata_player_json['floor'] + + def __str__(self): + return f"Player(name={self.name}, ready={self.ready}, points={self.points}, pattern_lines={self.pattern_lines}, wall={self.wall}, floor={self.floor})" + +class Bag: + def __init__(self, bag_json): + self.blue = bag_json['blue'] + self.yellow = bag_json['yellow'] + self.red = bag_json['red'] + self.black = bag_json['black'] + self.white = bag_json['white'] + + def __str__(self): + return f"Bag(blue={self.blue}, yellow={self.yellow}, red={self.red}, black={self.black}, white={self.white})" + +class Factory: + def __init__(self, factory_json): + self.blue = factory_json['blue'] + self.yellow = factory_json['yellow'] + self.red = factory_json['red'] + self.black = factory_json['black'] + self.white = factory_json['white'] + + def __str__(self): + return f"Factory(blue={self.blue}, yellow={self.yellow}, red={self.red}, black={self.black}, white={self.white})" + +class Market: + def __init__(self, market_json): + self.start = market_json['start'] + self.blue = market_json['blue'] + self.yellow = market_json['yellow'] + self.red = market_json['red'] + self.black = market_json['black'] + self.white = market_json['white'] + + def __str__(self): + return f"Market(start={self.start}, blue={self.blue}, yellow={self.yellow}, red={self.red}, black={self.black}, white={self.white})" + +class PatternLine: + def __init__(self, pattern_line_json): + self.color = pattern_line_json['color'] + self.number = pattern_line_json['number'] + + def __str__(self): + return f"PatternLine(color={self.color}, number={self.number})" + +class Wall: + def __init__(self, wall_json): + self.wall = wall_json + def __str__(self): + return f"Wall(wall={self.wall})" + +class Floor: + def __init__(self, floor_json): + self.start = floor_json['start'] + self.blue = floor_json['blue'] + self.yellow = floor_json['yellow'] + self.red = floor_json['red'] + self.black = floor_json['black'] + self.white = floor_json['white'] + + def __str__(self): + return f"Floor(start={self.start}, blue={self.blue}, yellow={self.yellow}, red={self.red}, black={self.black}, white={self.white})" + +class GameState: + def __init__(self, gamedata_json): + json = gamedata_json + self.n_players = json['n_players'] + self.current_player = json['current_player'] + self.starting_player = json['starting_player'] + self.player_names = json['player_names'] + self.game_end = json['game_end'] + self.rounds = json['rounds'] + self.days = json['days'] + self.bag = Bag(json['bag']) + self.lid = Bag(json['lid']) # Assuming lid has the same structure as bag + self.factories = [Factory(factory_json) for factory_json in json['factories']] + self.market = Market(json['market']) + self.players = [Player(name, player_json) for name, player_json in json['players'].items()] + def __str__(self): + return f"GameState(n_players={self.n_players}, current_player={self.current_player}, starting_player={self.starting_player}, player_names={self.player_names}, game_end={self.game_end}, rounds={self.rounds}, days={self.days}, bag={self.bag}, lid={self.lid}, factories={self.factories}, market={self.market}, players={self.players})" + +def init_game(names=["a", "b"]): + #initialize a game with the given names and return the game_id + player_names = {"player_names": names} + response = requests.post(ozai_url + 'game', json=player_names) + if response.status_code == 200: + game_id = response.json() + print("Game ID:", game_id) + return game_id + else: + return None + +def join_game(game_id, player_name): + #gets a initialized game and joins the game with the given player name + response = requests.get(ozai_url + 'game/' + game_id + '?player=' + player_name) + if response.status_code != 200: + return None + players = response.json().get('players') + return players + +def create_game(names=["a", "b"]): + # Create a game with the players given in names, by initializing it and joining the players + game_id = init_game(names) + for name in names: + join_game(game_id, name) + return game_id + +def submit_action(game_id, player_name, source=0, destination=0, color="start", policy="strict"): + # Submit an action to the game. The action is a dictionary with the keys being the action type and the values being the arguments. + # Example: {"player": 0, "market": true, "factory": 0, "color": "blue", "patternLine": 0} + if source != "market": + if source < 0 or source > 4: + return False + if destination != "floor": + if destination < 0 or destination > 4: + return False + action = { + 'player': str(player_name), + 'policy': policy, + 'color': str(color).lower(), + 'source': source, + 'destination': destination + } + response = requests.put(ozai_url + 'game/' + game_id, json=action) + if response.status_code == 200: + return True + else: + return False + +def get_gamestate(game_id) -> GameState: + response = requests.get(ozai_url + 'game/' + game_id) + if response.status_code == 200: + game_state = response.json() + game_state = GameState(game_state) + return game_state + else: + return None + +def get_score(game_id, player_name): + GameState = get_gamestate(game_id) + for player in GameState.players: + if player_name == player.name: + return player.points + +def game_over(game_id): + try: + GameState = get_gamestate(game_id) + return GameState.game_end + except: + return False + +def get_all_valid_moves(game_id, player_name): + # Get the game state + GameState = get_gamestate(game_id) + # Generate a list of all possible moves + sources = ["market"] + [i for i, factory in enumerate(GameState.factories) if any([factory.blue, factory.yellow, factory.red, factory.black, factory.white])] + colors = ["blue", "yellow", "red", "black", "white"] + destinations = ["floor"] + list(range(len(GameState.factories))) + valid_moves = [] + for source in sources: + for color in colors: + for destination in destinations: + # Check if the source has the color we want to move + if source == "market": + amount = getattr(GameState.market, color) + if amount > 0: + valid_moves.append((source, destination, color, amount)) + else: + amount = getattr(GameState.factories[source], color) + if amount > 0: + valid_moves.append((source, destination, color, amount)) + + # Filter out invalid destinations if the destinaiton is a pattern line, and the wall contains the color + wall_colors = [ + ['blue', 'yellow', 'red', 'black', 'white'], + ['white', 'blue', 'yellow', 'red', 'black'], + ['black', 'white', 'blue', 'yellow', 'red'], + ['red', 'black', 'white', 'blue', 'yellow'], + ['yellow', 'red', 'black', 'white', 'blue'], + ] + moves = [] + player = [player for player in GameState.players if player.name == player_name][0] + for move in valid_moves: + if move[1] == "floor": + moves.append(move) + else: + # print(player.pattern_lines[move[1]]) + #if element in wall is tru convert it to the correct color + player.wall = [[wall_colors[i][j] if player.wall[i][j] else False for j in range(5)] for i in range(5)] + #check that the wall does not contain the color on the same row as the pattern line, and that the pattern line does not contain another color + if not move[2] in player.wall[move[1]]: + if player.pattern_lines[move[1]]['color'] == move[2] or player.pattern_lines[move[1]]['color'] == None: + moves.append(move) + valid_moves = moves + return valid_moves + + +### +# strategies for filtering in the do_move solution. +### +def strategy_1(move, GameState,player): + #strategy 1 most fit always 530points 27-3 + if move[1] == 'floor': + return True + remaining_space = move[1] + 1 - player.pattern_lines[move[1]]['number'] + if move[3] >= remaining_space-1 and move[3] <= remaining_space +1: + return True + else: + return False + +def strategy_2(move,GameState,player): + #strategy 2 tierdoff fitting. 983 30-0 + if move[1] == 'floor': + return True + remaining_space = move[1] + 1 - player.pattern_lines[move[1]]['number'] + + if move[1] == 4: + if move[3] <= remaining_space and move[3] >= remaining_space -2: + return True + if move[1] == 3: + if move[3] <= remaining_space and move[3] >= remaining_space -1: + return True + if move[1] < 3: + if move[3] <= remaining_space and move[3] >= remaining_space: + return True + return False + +def strategy_random(move, GameState,player): + return True #do not filter any moves + +def do_move(game_id, player_name, filter_strategy=strategy_random): + moves = get_all_valid_moves(game_id, player_name) + try: + GameState = get_gamestate(game_id) + player = [player for player in GameState.players if player.name == player_name][0] + filtered_moves = [] + for move in moves: + if filter_strategy(move,GameState,player): + filtered_moves.append(move) + # Submit a random move, of the filtered ones. + move = random.choice(filtered_moves) + submit_action(game_id, player_name, move[0], move[1], move[2]) + return move + except: + #if filtered all moves, just submit a random move from the moves + result = random.choice(moves) + return result + +def play_game(gameid, players, strategy): + print(f"Playing game {gameid} with players {players} and strategy {strategy.__name__}") + while not game_over(gameid): + for player in players: + mov = do_move(gameid, player, filter_strategy=strategy) + if game_over(game_id): + print(f"Game Over {player} won") + break + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description='Play a game with specified strategies.') + parser.add_argument('--game_id', type=int, default=0, help='The id of the game to play.') + parser.add_argument('--players', nargs='+', default=["a", "b"], help='The names of the players.') + parser.add_argument('--strategy', type=str, default="", help='The strategy to use. Can be "1", "2", or "" for random.') + parser.add_argument('--ozai_url', type=str, default='http://localhost:8000/api/', help='The url to the ozai server.') + + args = parser.parse_args() + game_id = args.game_id + players = args.players + strategy = args.strategy + #use global ozai url and update it + ozai_url = args.ozai_url + + if game_id == 0: + game_id = create_game(names=players) + if strategy == "1": + play_game(game_id, players, strategy_1) + elif strategy == "2": + play_game(game_id, players, strategy_2) + else: + play_game(game_id, players, strategy_random) \ No newline at end of file