mirror of
https://github.com/kristoferssolo/Tetris.git
synced 2025-10-21 20:00:35 +00:00
refactor(ai)
This commit is contained in:
parent
c8859bc571
commit
77cd153b10
8
config
8
config
@ -1,7 +1,7 @@
|
||||
[NEAT]
|
||||
fitness_criterion = max
|
||||
fitness_threshold = 0
|
||||
pop_size = 100
|
||||
fitness_threshold = 500
|
||||
pop_size = 50
|
||||
reset_on_extinction = False
|
||||
|
||||
[DefaultGenome]
|
||||
@ -44,8 +44,8 @@ node_add_prob = 0.2
|
||||
node_delete_prob = 0.2
|
||||
|
||||
# network parameters
|
||||
num_hidden = 3
|
||||
num_inputs = 216
|
||||
num_hidden = 1
|
||||
num_inputs = 200
|
||||
num_outputs = 6
|
||||
|
||||
# node response options
|
||||
|
||||
7
main.py
7
main.py
@ -61,14 +61,13 @@ def main(args: argparse.ArgumentParser) -> None:
|
||||
elif args.verbose:
|
||||
CONFIG.log_level = "info"
|
||||
|
||||
# import ai
|
||||
import ai
|
||||
import game
|
||||
|
||||
if args.train is not None:
|
||||
# ai.log.debug("Training the AI")
|
||||
# # ai.train(*args.train)
|
||||
ai.log.debug("Training the AI")
|
||||
ai.train(*args.train)
|
||||
# game.Menu(GameMode.AI_TRAINING).run()
|
||||
pass
|
||||
else:
|
||||
game.log.debug("Running the game")
|
||||
game.Main(GameMode.PLAYER).run()
|
||||
|
||||
@ -6,55 +6,59 @@ import numpy as np
|
||||
import pygame
|
||||
from game import Main
|
||||
from game.sprites import Block
|
||||
from utils import CONFIG
|
||||
from utils import CONFIG, GameMode
|
||||
|
||||
from .fitness import calculate_fitness
|
||||
# from .fitness import calculate_fitness
|
||||
from .log import log
|
||||
from .moves import calculate_fitness
|
||||
|
||||
|
||||
def eval_genome(genome: neat.DefaultGenome, config: neat.Config) -> float:
|
||||
app = Main()
|
||||
app = Main(GameMode.AI_TRAINING).play()
|
||||
|
||||
app.mute()
|
||||
game = app.game
|
||||
tetris = game.tetris
|
||||
net = neat.nn.FeedForwardNetwork.create(genome, config)
|
||||
genome.fitness = 0
|
||||
|
||||
while not game.game_over:
|
||||
current_figure: list[int] = [
|
||||
component
|
||||
for block in game.tetromino.blocks
|
||||
for component in (int(block.pos.x), int(block.pos.y))
|
||||
]
|
||||
while not tetris.game_over:
|
||||
# current_figure: list[int] = [
|
||||
# component
|
||||
# for block in tetris.tetromino.blocks
|
||||
# for component in (int(block.pos.x), int(block.pos.y))
|
||||
# ]
|
||||
|
||||
next_figure: list[int] = [
|
||||
vec
|
||||
for vec in app.next_figures[0].value.shape
|
||||
for vec in (int(vec.x), int(vec.y))
|
||||
]
|
||||
# next_figure: list[int] = [
|
||||
# vec
|
||||
# for vec in game.next_figure.value.shape
|
||||
# for vec in (int(vec.x), int(vec.y))
|
||||
# ]
|
||||
|
||||
field = np.where(game.field != None, 1, 0)
|
||||
field = np.where(tetris.field != None, 1, 0)
|
||||
|
||||
output = net.activate((*next_figure, *current_figure, *field.flatten()))
|
||||
for block in tetris.tetromino.blocks:
|
||||
field[int(block.pos.y), int(block.pos.x)] = 2
|
||||
|
||||
output = net.activate(field.flatten())
|
||||
|
||||
decision = output.index(max(output))
|
||||
|
||||
decisions = {
|
||||
0: game.move_left,
|
||||
1: game.move_right,
|
||||
2: game.move_down,
|
||||
3: game.rotate,
|
||||
4: game.rotate_reverse,
|
||||
5: game.drop,
|
||||
0: tetris.move_left,
|
||||
1: tetris.move_right,
|
||||
2: tetris.move_down,
|
||||
3: tetris.rotate,
|
||||
4: tetris.rotate_reverse,
|
||||
5: tetris.drop,
|
||||
}
|
||||
|
||||
decisions[decision]()
|
||||
app.run_game_loop()
|
||||
|
||||
genome.fitness = calculate_fitness(field)
|
||||
score, lines, level = app.game.score, app.game.lines, app.game.level
|
||||
score, lines, level = tetris.score, tetris.lines, tetris.level
|
||||
|
||||
log.debug(f"{genome.fitness=:<+6.6}\t{score=:<6} {lines=:<6} {level=:<6}")
|
||||
|
||||
game.restart()
|
||||
tetris.restart()
|
||||
return genome.fitness
|
||||
|
||||
3
src/ai/moves/__init__.py
Normal file
3
src/ai/moves/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
from .calculate import calculate_fitness
|
||||
|
||||
__all__ = ["calculate_fitness"]
|
||||
35
src/ai/moves/calculate.py
Normal file
35
src/ai/moves/calculate.py
Normal file
@ -0,0 +1,35 @@
|
||||
from typing import Optional
|
||||
|
||||
import numpy as np
|
||||
|
||||
from ai.log import log
|
||||
|
||||
from .bumpiness import bumpiness
|
||||
from .height import aggregate_height
|
||||
from .holes import holes
|
||||
from .lines import complete_lines
|
||||
|
||||
|
||||
def calculate_fitness(field: np.ndarray) -> float:
|
||||
"""
|
||||
Calculate the fitness value for the given field.
|
||||
|
||||
Args:
|
||||
field: The game field.
|
||||
|
||||
Returns:
|
||||
The fitness value.
|
||||
"""
|
||||
|
||||
height_w = aggregate_height(field)
|
||||
holes_w = holes(field)
|
||||
bumpiness_w = bumpiness(field)
|
||||
lines_w = complete_lines(field)
|
||||
|
||||
fitness = (
|
||||
-0.510066 * height_w
|
||||
+ 0.760666 * lines_w
|
||||
- 0.35663 * holes_w
|
||||
- 0.184483 * bumpiness_w
|
||||
)
|
||||
return fitness
|
||||
@ -23,7 +23,7 @@ class Game(BaseScreen):
|
||||
game: Game object.
|
||||
score: Score object.
|
||||
preview: Preview object.
|
||||
next_figures: List of upcoming figures.
|
||||
next_figure: List of upcoming figures.
|
||||
music: Music that plays in the background.
|
||||
"""
|
||||
|
||||
|
||||
@ -13,7 +13,7 @@ from .game import Game
|
||||
|
||||
class Main(BaseScreen, SceenElement, TextScreen):
|
||||
def __init__(self, mode: GameMode) -> None:
|
||||
log.info("Initializing the game")
|
||||
# log.info("Initializing the game")
|
||||
self._initialize_pygame()
|
||||
self._initialize_surface()
|
||||
self._initialize_rect()
|
||||
@ -61,7 +61,7 @@ class Main(BaseScreen, SceenElement, TextScreen):
|
||||
|
||||
def exit(self) -> None:
|
||||
"""Exit the game."""
|
||||
log.info("Exiting the game")
|
||||
# log.info("Exiting the game")
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
|
||||
|
||||
@ -150,8 +150,8 @@ class Tetris(BaseScreen):
|
||||
self._check_finished_rows()
|
||||
|
||||
self.game_over: bool = self._check_game_over()
|
||||
if self.game_over:
|
||||
self.restart()
|
||||
# if self.game_over:
|
||||
# self.restart()
|
||||
|
||||
self.tetromino = Tetromino(
|
||||
self.sprites,
|
||||
@ -171,13 +171,13 @@ class Tetris(BaseScreen):
|
||||
"""
|
||||
for block in self.tetromino.blocks:
|
||||
if block.pos.y <= 0:
|
||||
log.info("Game over!")
|
||||
# log.info("Game over!")
|
||||
return True
|
||||
return False
|
||||
|
||||
def restart(self) -> None:
|
||||
"""Restart the game."""
|
||||
log.info("Restarting the game")
|
||||
# log.info("Restarting the game")
|
||||
self._reset_game_state()
|
||||
self._initialize_field_and_tetromino()
|
||||
self.game_over = False
|
||||
|
||||
@ -21,7 +21,7 @@ class Game:
|
||||
size: Size = Size(columns * cell.width, rows * cell.width)
|
||||
pos: Vec2 = Vec2(padding, padding)
|
||||
offset: Vec2 = Vec2(columns // 2, -1)
|
||||
initial_speed: float | int = 300
|
||||
initial_speed: float | int = 100
|
||||
movment_delay: int = 150
|
||||
rotation_delay: int = 200
|
||||
drop_delay: int = 200
|
||||
|
||||
Loading…
Reference in New Issue
Block a user