From b972a2e396ab4c0485178f6b6dec9b2c7e6877f6 Mon Sep 17 00:00:00 2001 From: Eduard Prokhorikhin Date: Mon, 8 Apr 2024 20:04:36 +0200 Subject: [PATCH 1/2] feat: added maxHeight heuristic and tests --- main.py | 31 +++++++++++++++++++++++++++++++ src/agents/heuristic.py | 29 ++++++++++------------------- src/game/block.py | 2 -- src/game/board.py | 2 +- test/agents/test_heuristic.py | 28 ++++++++++++++++++++++++++++ 5 files changed, 70 insertions(+), 22 deletions(-) create mode 100644 main.py create mode 100644 test/agents/test_heuristic.py diff --git a/main.py b/main.py new file mode 100644 index 0000000..19df049 --- /dev/null +++ b/main.py @@ -0,0 +1,31 @@ +from src.game.board import Board +from src.agents.heuristic import * + +def test_heuristic(): + board = Board() + board.board =\ + [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 0, 1, 0, 0, 0, 0, 0, 0], + [1, 0, 0, 0, 0, 0, 1, 0, 1, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 1, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]] + + print(aggregate_heights(board)) + print(max_height(board)) + +test_heuristic() \ No newline at end of file diff --git a/src/agents/heuristic.py b/src/agents/heuristic.py index 22b8b43..88080ef 100644 --- a/src/agents/heuristic.py +++ b/src/agents/heuristic.py @@ -14,24 +14,15 @@ def aggregate_heights(gameState: Board) -> int: for j in range(gameState.columns): if gameState.board[i][j] > 0: if checkedList[j] == 0: - checkedList[j] = gameState.rows - i+1 + checkedList[j] = gameState.rows - i return sum(checkedList) - - - -if __name__ == "__main__": - board = Board() - board.board =\ - [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 1, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 1, 0, 1, 0, 0, 0, 0, 0, 0], - [1, 0, 0, 0, 0, 0, 1, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 1, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]] - - assert aggregate_heights(board) == 27 \ No newline at end of file +def max_height(gameState: Board) -> int: + """ Returns the maximum height of the columns in the game state. """ + checkedList = [0 for i in range(gameState.columns)] + for i in range(gameState.rows): + for j in range(gameState.columns): + if gameState.board[i][j] > 0: + if checkedList[j] == 0: + checkedList[j] = gameState.rows - i + return max(checkedList) \ No newline at end of file diff --git a/src/game/block.py b/src/game/block.py index e000471..5de2b16 100644 --- a/src/game/block.py +++ b/src/game/block.py @@ -1,7 +1,5 @@ import random -import random - class Block: def __init__(self, x, y, blockType): diff --git a/src/game/board.py b/src/game/board.py index 9b1b5d5..4c39131 100644 --- a/src/game/board.py +++ b/src/game/board.py @@ -1,7 +1,7 @@ import pygame import random import copy -from block import Block +from src.game.block import Block ''' Denne skriver ut brettet i terminal bare. diff --git a/test/agents/test_heuristic.py b/test/agents/test_heuristic.py new file mode 100644 index 0000000..0bd6551 --- /dev/null +++ b/test/agents/test_heuristic.py @@ -0,0 +1,28 @@ +from src.game.board import Board +from src.agents.heuristic import aggregate_heights + +def test_heuristic(): + board = Board() + board.board =\ + [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 0, 1, 0, 0, 0, 0, 0, 0], + [1, 0, 0, 0, 0, 0, 1, 0, 1, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 1, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]] + + assert aggregate_heights(board) == 27 \ No newline at end of file From dde26dd9c8eff45a274bce2c5dbcc760a85facb6 Mon Sep 17 00:00:00 2001 From: HFossdal <122976119+HFossdal@users.noreply.github.com> Date: Mon, 8 Apr 2024 20:04:59 +0200 Subject: [PATCH 2/2] feat: :sparkles: Implemented Hard walls for moving left and right Implemented Hard walls for moving left and right --- src/game/block.py | 2 +- src/game/board.py | 65 ++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 60 insertions(+), 7 deletions(-) diff --git a/src/game/block.py b/src/game/block.py index e000471..bcf6fbe 100644 --- a/src/game/block.py +++ b/src/game/block.py @@ -46,7 +46,7 @@ def setCoordinates(self, x, y): def rotateLeft(self, undo: bool = False): if not undo: - self.rotation = (self.rotation - 1) % len(self.figures[self.type]) + self.rotation = (self.rotation - 1) % len(self.figures[self.type]) else: self.rotateRight() diff --git a/src/game/board.py b/src/game/board.py index 9b1b5d5..7e25a45 100644 --- a/src/game/board.py +++ b/src/game/board.py @@ -48,7 +48,7 @@ def checkCharacter(self, character): def rotateBlockRight(self): - if self.validMove(self.block.rotateRight): + if self.validRotation(self.block.rotateRight): self.placeBlock() @@ -104,12 +104,30 @@ def blockLanded(self): # self.board[i + self.block.y][j + self.block.x] > 0: # return False # Returnerer False ved kollisjon eller ugyldig trekk - def validMove(self, simulateimulatedMove): + def validMove(self, simulatedMove): # if simulated move fails = move out of bounds and should be disallowed - simulateimulatedMove() + block_indices = [i for i in range(16) if i in self.block.image()] - for row in range(4, 1, -1): - for column in range(4, 1, -1): + moving_direction = [0, 0] + moving_direction[0] = 1 if simulatedMove == self.block.moveRight else -1 if simulatedMove == self.block.moveLeft else 0 + moving_direction[1] = 1 if simulatedMove == self.block.moveDown else 0 + + for index in block_indices: + print(self.block.y + (index // 4) + moving_direction[1], self.rows - 1) + print(self.block.x + (index % 4) + moving_direction[0], self.columns - 1) + if ( + self.block.y + (index // 4) + moving_direction[1] < 0 or + self.block.y + (index // 4) + moving_direction[1] > self.rows - 2 or + self.block.x + (index % 4) + moving_direction[0] < 0 or + self.block.x + (index % 4) + moving_direction[0] > self.columns - 2 + ): + + return False + + simulatedMove() + + for row in range(4): + for column in range(4): if row * 4 + column in self.block.image(): if ( row + self.block.y > self.rows - 1 or @@ -118,11 +136,46 @@ def validMove(self, simulateimulatedMove): column + self.block.x < 0 or self.prevBoard[row + self.block.y][column + self.block.x] > 0 ): - simulateimulatedMove(Undo = True) + simulatedMove(Undo = True) return False return True # Return True if the move is valid + def validRotation(self, simulatedRotation): + # if simulated move fails = move out of bounds and should be disallowed + block_indices = [i for i in range(16) if i in self.block.image()] + rotation = 1 if simulatedRotation.__name__ == 'rotateRight' else -1 if simulatedRotation.__name__ == 'rotateLeft' else 0 + new_block_indices = self.figures[self.type][(self.rotation + rotation) % 4] + + # TODO: Implement valid rotation check similar to validMove + # Should push the block to closest valid position for the rotation in a given radius (but not able to go through walls) + + + for index in block_indices: + if ( + self.block.y + (index // 4) < 0 or + self.block.y + (index // 4) > self.rows - 2 or + self.block.x + (index % 4) < 0 or + self.block.x + (index % 4) > self.columns - 2 + ): + return False + + simulatedRotation() + + for row in range(4): + for column in range(4): + if row * 4 + column in self.block.image(): + if ( + row + self.block.y > self.rows - 1 or + row + self.block.y < 0 or + column + self.block.x > self.columns - 1 or + column + self.block.x < 0 or + self.prevBoard[row + self.block.y][column + self.block.x] > 0 + ): + simulatedRotation(Undo = True) + return False + return True + def clearRow(self, rownumber): # Fjerner den angitte raden og legger til en ny tom rad ved bunnen av matrisen newMatrix = self.board[:rownumber] + self.board[rownumber+1:]