Last active
March 22, 2023 03:03
-
-
Save JackDraak/2167aff9780b36b16dd83c2ddec9934d to your computer and use it in GitHub Desktop.
Python Fifteen Game - console or pyGame GUI
This file contains 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
from game_controller import GameController | |
class AIController(GameController): | |
def __init__(self): | |
super().__init__() | |
self.player_name = "Fred" # This can be changed to whatever name you prefer | |
self.is_ai = True | |
def get_move(self): | |
# Implement logic here to calculate the best move based on the current game state | |
# and return a tuple (row, col) with the coordinates of the move. | |
# For example, you could use your language model to generate a move based on the current board state. | |
# For now, let's assume that we always choose the first empty cell as our move: | |
for row in range(self.game_state.board_size): | |
for col in range(self.game_state.board_size): | |
if self.game_state.board[row][col] == "": | |
return (row, col) | |
# If no empty cell is found, return None | |
return None |
This file contains 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
from Game import Game | |
def command_check(command: str): | |
if command == "": # Default behaviour selected. | |
return command | |
else: | |
direction = (0, 0) | |
check_command = command.lower()[0] | |
if check_command == "q": # Quit. | |
quit_game() | |
elif check_command == "a": # Move blank left. (tile on left takes blank position) | |
direction = (0, -1) | |
elif check_command == "d": # Move blank right. (tile on right takes blank position) | |
direction = (0, 1) | |
elif check_command == "w": # Move blank up. (tile above takes blank position) | |
direction = (-1, 0) | |
elif check_command == "s": # Move blank down. (tile below takes blank position) | |
direction = (1, 0) | |
if direction != (0, 0): | |
return direction | |
return command | |
def input_game_size(): | |
size_default = 4 # For the classic '15 puzzle', use a grid with a dimension of 4. | |
size_max = 31 # Grids with dimension >31 have >1000 tiles, would require re-formatting. | |
size_min = 3 # Grids with dimension <3 are not functionally playable. | |
size = size_default | |
print("\nGoal: slide the game tiles into the open position, 1-by-1, to re-order them. [Or (q)uit at any prompt]") | |
print("To play the classic tile game, '15', ", end="") | |
valid_input = False | |
while not valid_input: | |
grid_size = input(f"please choose a grid size from {size_min} to {size_max} [default: {size_default}] ") | |
grid_size = command_check(grid_size) | |
if grid_size == "": # Default value selected. | |
valid_input = True | |
elif type(grid_size) == tuple: # Reject WASD input; unrelated to game_size | |
pass | |
elif grid_size.isdigit(): | |
size = int(grid_size) | |
if size_min <= size <= size_max: | |
valid_input = True | |
print() | |
return size | |
def input_shuffle(game: Game): | |
print("*** Congratulations, you solved the puzzle! ***\n") | |
print(game) | |
shuffled = False | |
while not shuffled: | |
shuffles = input(f"How many times would you like to shuffle? [default: {game.shuffle_default}] \n") | |
shuffles = command_check(shuffles) | |
if shuffles == "": # Default value selected. | |
game.shuffle(game.shuffle_default) | |
shuffled = True | |
pass | |
elif type(shuffles) == tuple: # Reject WASD input; unrelated to shuffling | |
pass | |
elif not shuffles.isdigit(): # Reject non-integer input; has no meaning | |
pass | |
else: | |
shuffled = game.shuffle(int(shuffles)) | |
def input_turn(game: Game): | |
print(game) | |
player_move = input("Please, enter the label of the tile you would like to push into the gap.\n" + | |
f"Valid tiles to move: {game.get_valid_moves()} ") | |
player_move = command_check(player_move) | |
process_turn(game, player_move) | |
def play(game: Game): | |
while True: | |
input_turn(game) | |
if game.is_solved(): | |
input_shuffle(game) | |
def process_turn(game: Game, player_move): | |
print() | |
if type(player_move) == tuple: | |
wasd_label = game.get_cardinal_label(player_move) | |
if game.get_valid_moves().__contains__(wasd_label): | |
game.slide_tile(int(wasd_label)) | |
else: | |
print("Unable to move that direction...\n") | |
elif not player_move.isdigit(): | |
print("Please, input a valid tile number to move...\n") | |
elif not game.slide_tile(int(player_move)): | |
print(f"Unable to move tile {player_move}...\n") | |
def quit_game(): | |
print("\nThank you for playing 'fifteen'. Have a nice day! ") | |
quit() | |
if __name__ == '__main__': | |
play(Game(input_game_size(), True)) |
This file contains 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
from Tile import Tile | |
import random | |
import usage | |
class Game: | |
def __init__(self, dimension: int, shuffled: bool): | |
entropy_factor = 100 | |
self.dimension = dimension | |
self.blank_label = dimension * dimension | |
self.blank_position = dimension - 1, dimension - 1 | |
self.shuffle_default = dimension * entropy_factor | |
self.solution = list(range(1, self.blank_label + 1)) | |
self.tiles = self.generate_tiles(dimension) | |
if shuffled: | |
self.shuffle(self.shuffle_default) | |
def __repr__(self): | |
print_string = "" | |
for x in range(self.dimension): | |
print_string += "\t" | |
for y in range(self.dimension): | |
label = self.get_label(x, y) | |
if label != self.blank_label: | |
print_string += f"\t{label}" | |
else: | |
print_string += "\t" | |
print_string += "\n" | |
return print_string | |
# TODO fix or replace this function; it creates Game objects that lose state (extra Tile sans properties, post-move) | |
def duplicate(self): | |
duplicate_game = Game(self.dimension, False) | |
duplicate_game.import_tiles(self.export_tiles()) # Issues may lie here with export/import? | |
return duplicate_game | |
def export_tiles(self): | |
tiles = list() | |
for tile in self.tiles: | |
tiles.append(Tile(tile.label, tile.row, tile.column, tile.dimension)) | |
return tiles | |
@staticmethod | |
def generate_tiles(dimension: int): | |
tiles = list() | |
label = 0 | |
for row in range(dimension): | |
for column in range(dimension): | |
label += 1 | |
tiles.append(Tile(label, row, column, dimension)) | |
return tiles | |
def get_cardinal_label(self, direction: tuple): | |
delta = (direction[0] + self.blank_position[0]), (direction[1] + self.blank_position[1]) | |
return self.get_label(delta[0], delta[1]) # Return tile.label based on position delta:blank | |
def get_distance_by_label(self, label: int): | |
for tile in self.tiles: | |
if tile.label == label: | |
return tile.distance() | |
return False | |
def get_distance_set(self): | |
label_pairs = list() | |
for row in range(self.dimension): | |
for column in range(self.dimension): | |
pair = list() | |
label = self.get_label(row, column) | |
this_pair = label, self.get_distance_by_label(label) | |
pair.append(this_pair) | |
label_pairs.append(pair) | |
return label_pairs | |
def get_distance_sum(self): | |
return sum(tile.distance() for tile in self.tiles) | |
def get_label(self, row: int, column: int): | |
for tile in self.tiles: | |
if tile.row == row and tile.column == column: | |
return tile.label | |
def get_labels_as_list(self): # Return tile-set labels as a 1D array. | |
tiles = list() | |
for row in range(self.dimension): | |
for column in range(self.dimension): | |
tiles.append(self.get_label(row, column)) | |
return tiles | |
def get_labels_as_matrix(self): # Return tile-set labels as a 2D array. | |
tiles = list() | |
for row in range(self.dimension): | |
rows = list() | |
for column in range(self.dimension): | |
rows.append(self.get_label(row, column)) | |
tiles.append(rows) | |
return tiles | |
def get_position(self, label: int): | |
for tile in self.tiles: | |
if tile.label == label: | |
return tile.row, tile.column | |
def get_valid_moves(self): | |
valid_moves = list() | |
blank_row, blank_column = self.blank_position | |
for tile in self.tiles: | |
if tile.row == blank_row: # Select horizontal neighbors. | |
if tile.column + 1 == blank_column or tile.column - 1 == blank_column: | |
valid_moves.append(tile.label) | |
if tile.column == blank_column: # Select vertical neighbors. | |
if tile.row + 1 == blank_row or tile.row - 1 == blank_row: | |
valid_moves.append(tile.label) | |
if valid_moves.__contains__(self.blank_label): # Trim blank-tile from set. | |
valid_moves.remove(self.blank_label) | |
return valid_moves | |
def import_tiles(self, tiles: list): | |
self.tiles = list() | |
self.tiles = tiles | |
def is_solved(self): | |
return self.solution == self.get_labels_as_list() | |
def print_tile_set(self): | |
for tile in self.tiles: | |
lab = tile.label | |
car = tile.cardinal | |
dim = tile.dimension | |
row = tile.row | |
col = tile.column | |
dis = self.get_distance_by_label(tile.label) | |
print(f"<Tile> label:{lab}({car}), position:({dim}){row},{col} distance:{dis}") | |
def set_tile_position(self, label: int, row: int, column: int): | |
for tile in self.tiles: | |
if tile.label == label: | |
tile.move_to(row, column) | |
return True | |
return False | |
def shuffle(self, moves: int): | |
last_move = int() | |
while moves > 0: | |
options = self.get_valid_moves() | |
if options.__contains__(last_move): | |
options.remove(last_move) | |
random_move = options[random.randint(0, len(options) - 1)] | |
self.slide_tile(random_move) | |
last_move = random_move | |
moves -= 1 | |
return True | |
def slide_tile(self, label: int): | |
if self.get_valid_moves().__contains__(label): | |
this_blank_position = self.blank_position | |
this_tile_pos = self.get_position(label) | |
if not self.set_tile_position(label, this_blank_position[0], this_blank_position[1]): # Set pos of tile. | |
print(f"\n{self}Game.set_tile_position({label},{this_blank_position[0]},{this_blank_position[1]}) FAIL") | |
return False | |
if not self.set_tile_position(self.blank_label, this_tile_pos[0], this_tile_pos[1]): # Set pos of blank. | |
print(f"\n{self}Game.set_tile_position({self.blank_label},{this_tile_pos[0]},{this_tile_pos[1]}) FAIL") | |
return False | |
else: | |
self.blank_position = this_tile_pos[0], this_tile_pos[1] | |
return True | |
return False | |
if __name__ == '__main__': | |
usage.explain() |
This file contains 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
Here's some example code for a machine learning controller that trains a model based on the game data: | |
import pandas as pd | |
from sklearn.ensemble import RandomForestClassifier | |
class MLController: | |
def __init__(self, data_file): | |
self.data_file = data_file | |
def train_model(self): | |
# Load data from file | |
data = pd.read_csv(self.data_file) | |
# Split data into features and labels | |
features = data.drop('label', axis=1) | |
labels = data['label'] | |
# Train a machine learning model | |
model = RandomForestClassifier() | |
model.fit(features, labels) | |
return model | |
This code assumes that you have game data stored in a CSV file, where each row represents a game state and includes features | |
such as the positions of pieces on the board, the current player, etc. The train_model method reads in this CSV file, | |
preprocesses the data by splitting it into features and labels, and trains a RandomForestClassifier model on the data. | |
You can modify this code as needed to work with your game and desired machine learning algorithms. |
This file contains 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
from game import Game | |
import numpy as np | |
from sklearn.neural_network import MLPClassifier | |
from sklearn.externals import joblib | |
class ml_controller: | |
def init(self): | |
# Initialize empty arrays to store game data | |
self.data_X = [] | |
self.data_y = [] | |
# Play a few games to collect training data | |
self.play_games(50) | |
# Train the model on the collected data | |
self.train_model() | |
def play_games(self, n): | |
for i in range(n): | |
game = Game() | |
while not game.is_game_over(): | |
# Replace the next line with your AI's decision-making algorithm | |
move = self.find_best_move(game) | |
game.make_move(move) | |
# Append the game data to the arrays | |
self.data_X.append(game.get_game_board()) | |
self.data_y.append(game.get_winner()) | |
def find_best_move(self, game): | |
# Replace this method with |
This file contains 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 pandas as pd | |
from sklearn.ensemble import RandomForestClassifier | |
class MLController: | |
def __init__(self, data_file): | |
self.data_file = data_file | |
def train_model(self): | |
# Load data from file | |
data = pd.read_csv(self.data_file) | |
# Split data into features and labels | |
features = data.drop('label', axis=1) | |
labels = data['label'] | |
# Train a machine learning model | |
model = RandomForestClassifier() | |
model.fit(features, labels) | |
return model |
This file contains 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 usage | |
class Tile: | |
def __init__(self, label: int, row: int, column: int, dimension: int): | |
self.cardinal = label | |
self.label = label | |
self.row = row | |
self.column = column | |
self.dimension = dimension | |
def __repr__(self): | |
lab = self.label | |
car = self.cardinal | |
dim = self.dimension | |
row = self.row | |
col = self.column | |
dis = self.distance() | |
return f"<Tile> label:{lab}({car}), position:({dim}){row},{col} distance:{dis}" | |
def distance(self): | |
lab = self.label | |
dim = self.dimension | |
row = self.row | |
col = self.column | |
row_dimension = row * dim | |
return abs(lab - col - row_dimension - 1) | |
def move_to(self, row: int, column: int): | |
self.row = row | |
self.column = column | |
def set(self, cardinal: int, label: int, row: int, column: int): | |
self.cardinal = cardinal | |
self.label = label | |
self.row = row | |
self.column = column | |
if __name__ == '__main__': | |
usage.explain() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Asking ChatGPT4 to help me update this code to train a ML model.....