#!/usr/bin/env python3 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): #get the score of the players score = [] for player in players: score.append(get_score(gameid, player)) print(f"Game Over, scores: {score}") return score 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)