def generate_dataset(game, output_file_name, total=100, mode="w", **kw): if output_file_name: output_file = open(output_file_name, mode) else: print(style("No output file?", Colours.FG.BRIGHT_RED)) return with HidePrintingContext(): # Concurrently (for total=100, takes around 36 seconds) with concurrent.futures.ProcessPoolExecutor(max_workers=8) as pool: for result in tqdm( pool.map(_setup_and_play_mp, ((gid, game.__class__, game.model, kw) for gid in range(total)), chunksize=50), ncols=79, desc="Generating data... ", total=total, ): print(result, file=output_file) # Sequentially (for total=100, takes around 1 minute) # for gid in tqdm(range(total), ncols=79, desc="Generating data... ", total=total): # result = setup_and_play(gid) # print(result, file=output_file) if output_file_name: print( style("Data saved to: '", Colours.FG.GREEN) + style(output_file_name, Colours.FG.BRIGHT_GREEN) + "'") output_file.close()
def print_accuracy(descr, win=0, draw=0, loss=0, result=None): if result is not None: win, draw, loss = result print(f"{descr}: " \ + style(f"{win * 100:.2f}% wins", Colours.FG.GREEN) + ", " \ + style(f"{draw * 100:.2f}% draws", Colours.FG.YELLOW) + ", " \ + style(f"{loss * 100:.2f}% losses", Colours.FG.RED) + ".")
def play_vs_ai(self, starting=None, legal_only=True, check_early_win=True, prevent_other_win=True, random_move_chance=0.0): # Against own model player = starting if starting is not None else starting_player() self._start_game(player, other_strat_descr="plays also as AI") while self.game.status is None: if player > 0 and random_move_chance > 0.0 and random.random( ) <= random_move_chance: move = self.game.random_action(legal_only=legal_only) else: move = self.predict(ai_player=player, check_early_win=check_early_win, prevent_other_win=prevent_other_win) if not self.game.is_legal_move(move): print( style("Illegal move from player! ", Colours.FG.BRIGHT_RED), player, move) print( f"{self.print_player_string(player)} adds to column {move}...") self.game.play_move(player, move) self._add_state(self.game, player, move) player = player * -1 print(f"{self.print_player_string(self.game.status)} wins!") return self.game.status
def play_vs_smart(self, starting=None, legal_only=True, n=100, check_early_win=True, prevent_other_win=True): # Against smart player player = starting if starting is not None else starting_player() self._start_game(player, other_strat_descr="plays smart") while self.game.status is None: if player < 0 and self.model: move = self.predict(check_early_win=check_early_win, prevent_other_win=prevent_other_win) else: move, p = self.game.smart_action(player, legal_only=legal_only, n=n) if not self.game.is_legal_move(move): print( style("Illegal move smart player! ", Colours.FG.BRIGHT_RED), player, move) print( f"{self.print_player_string(player)} adds to column {move}...") self.game.play_move(player, move) self._add_state(self.game, player, move) player = player * -1 print(f"{self.print_player_string(self.game.status)} wins!") return self.game.status
def __init__(self, board=None, print_friendly=False): super().__init__() self.game = Game( board=board) # Cannot do inheritance due to deepcopy in Game... self.states = [] self.model = None self.board_nodes = self.game.width * self.game.height self.input_shape = (self.board_nodes + 1, ) # If True, use another character for the other player so they are distinct # when printing without colours, e.g. ■ ▀ • ⦿ self.print_friendly = print_friendly self.__players = { -1: style("■", Colours.FG.YELLOW), 0: " ", 1: style("•" if self.print_friendly else "■", Colours.FG.RED) }
def load_existing_model(self, name, basepath="../data/models/"): try: self.model = load_model(f"{basepath}{name}", compile=True) self.model.predict(np.zeros( (1, *self.input_shape))) # Init predictor except Exception as e: self.model = None print(style(f"Could not load model!\n{e}", Colours.FG.RED)) else: self.model.summary()
def predict(self, ai_player=-1, check_early_win=True, prevent_other_win=True): # Get (move, chance) that AI wins ai_move = self._predict_move_probability(ai_player, check_early_win) if prevent_other_win: other_player_move = self._predict_move_probability( ai_player * -1, check_early_win) if other_player_move[1] > ai_move[1]: print(style("Trying to prevent", Colours.FG.BRIGHT_RED) \ + f" {self.print_player_string(ai_player * -1)} from winning...") return other_player_move[0] return ai_move[0]
def _predict_move_probability(self, player, check_early_win=True): # Predict chance of winning for each move # and return column with highest chance. max_probability = (0, 0.0) print(self.print_player_string(player) + ": Testing cols: |", end="") for move in range(self.game.width): if not self.game.is_legal_move(move): print(style(f" {0.0:.3f} |", Colours.FG.BRIGHT_RED), end="") continue test_game = Game(board=self.game.board.copy()) test_game.play_move(player, move) if check_early_win and test_game.status == player: # Win reached print(" win", end="") max_probability = (move, 1.0) break # Get prediction for move (make board positive by adding 1) test_input = np.concatenate( (test_game.board.flatten(), [player])).reshape( (1, *self.input_shape)) + 1 prediction = self.model.predict(test_input)[0][ player + 1] # [[player_0_prob, draw (?), player_1_prob]] if np.isnan(prediction): raise Exception("Error: prediction is NaN?") print(f" {prediction:.3f} |", end="") if prediction > max_probability[1]: max_probability = (move, prediction) print( f" => Predicted move at col {max_probability[0]} with {max_probability[1] * 100:.2f}%" ) return max_probability
# Build, train and save new model train_data, test_data = game.prepare_data(data_input, train_ratio=0.85) game.build_network(trained_name) game.train(train_data, test_data, epochs=15, batch_size=200, show_plot=True, save_plot_path=f"../data/models/{trained_name}.png") game.save_model(trained_name) else: # Or load pre-trained game.load_existing_model(trained_name) if not game.has_model(): print(style("No model ready! Exiting...", Colours.FG.BRIGHT_RED)) exit(1) check_early_win = True prevent_other_win = True if any((check_early_win, prevent_other_win)): print(style("Using options: ", Colours.FG.MAGENTA) \ + style("check_early_win", Colours.FG.BRIGHT_GREEN if check_early_win else Colours.FG.BRIGHT_BLACK) + ", "\ + style("prevent_other_win", Colours.FG.BRIGHT_GREEN if prevent_other_win else Colours.FG.BRIGHT_BLACK)) input( style("Model ready. Press enter to start tests or play.", Colours.FG.MAGENTA)) # Test games
def train(self, train_data, test_data, epochs=10, batch_size=200, show_plot=False, save_plot_path=""): print("Training model...") train_x, train_y = train_data hist = self.model.fit(train_x, train_y, validation_data=test_data, shuffle=True, epochs=epochs, batch_size=batch_size) test_score, test_acc = self.model.evaluate(test_data[0], test_data[1], verbose=0) print(style("Final accuracy on training set : ", Colours.FG.MAGENTA) \ + style(f"{hist.history['accuracy'][-1] * 100:.2f}%", Colours.FG.BRIGHT_MAGENTA)) print(style("Average accuracy while training: ", Colours.FG.MAGENTA) \ + style(f"{np.average(np.array(hist.history['val_accuracy'])) * 100:.2f}%", Colours.FG.BRIGHT_MAGENTA)) print(style("Average accuracy on test set : ", Colours.FG.MAGENTA) \ + style(f"{test_acc * 100:.2f}% (score={test_score:.4f})", Colours.FG.BRIGHT_MAGENTA)) if show_plot or save_plot_path: plt.style.use("ggplot") plt.figure() plt.plot( np.arange(0, epochs), [1.0] * epochs, "r--", label="Accuracy target", ) plt.plot(np.arange(0, epochs), hist.history["loss"], "cyan", label="train_loss") plt.plot(np.arange(0, epochs), hist.history["val_loss"], "blue", label="val_loss") plt.plot(np.arange(0, epochs), hist.history["accuracy"], "yellow", label="train_acc") plt.plot(np.arange(0, epochs), hist.history["val_accuracy"], "orange", label="val_acc") # Optionally plot other metrics? for k, v in hist.history.items(): if k not in ("loss", "val_loss", "accuracy", "val_accuracy"): plt.plot(np.arange(0, epochs), v, label=k) plt.title("Training Loss and Accuracy") plt.xlabel("Epoch #") plt.ylabel("Loss/Accuracy") plt.legend(loc="lower left") plt.xlim(0, epochs - 1) plt.ylim(bottom=0) if save_plot_path: plt.savefig(save_plot_path) if show_plot: plt.show() self.model.predict(np.zeros((1, *self.input_shape))) # Init predictor