From a365427f3af6b0daa7f1f9aa90d507d8f4f6fea7 Mon Sep 17 00:00:00 2001
From: yvandlo <44481260+yvandlo@users.noreply.github.com>
Date: Sat, 11 Dec 2021 21:43:30 -0500
Subject: [PATCH] Stochastic Tetris Successors
New tetris representation where states are just the grid, piece are object's reward function is mutable, constructor allows for a lot of customazation, and successor has every possible succesor for a hard drop for any rotation
---
.../inspectionProfiles/profiles_settings.xml | 6 -
.idea/vcs.xml | 6 -
.idea/workspace.xml | 91 --------
code/bot/simpletetris.py | 201 ++++++++++++++++++
4 files changed, 201 insertions(+), 103 deletions(-)
delete mode 100644 .idea/inspectionProfiles/profiles_settings.xml
delete mode 100644 .idea/vcs.xml
delete mode 100644 .idea/workspace.xml
create mode 100644 code/bot/simpletetris.py
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
deleted file mode 100644
index 105ce2d..0000000
--- a/.idea/inspectionProfiles/profiles_settings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
deleted file mode 100644
index 94a25f7..0000000
--- a/.idea/vcs.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
deleted file mode 100644
index 1490c5f..0000000
--- a/.idea/workspace.xml
+++ /dev/null
@@ -1,91 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 1638472057074
-
-
- 1638472057074
-
-
-
-
\ No newline at end of file
diff --git a/code/bot/simpletetris.py b/code/bot/simpletetris.py
new file mode 100644
index 0000000..5161d2b
--- /dev/null
+++ b/code/bot/simpletetris.py
@@ -0,0 +1,201 @@
+import random
+
+"Abstraction for Piece, Stores necessary data and some basic functions"
+class Piece:
+ def __init__(self, center, positions, label):
+ self.center = center
+ self.positions = positions
+ self.label = label
+
+ def rotate_left(self):
+ new_piece = []
+ anchor_x, anchor_y = self.center
+ for x, y in self.positions:
+ offset_x, offset_y = (x - anchor_x, y - anchor_y)
+ new_offset_x, new_offset_y = (offset_y, -1 * offset_x)
+ new_x, new_y = (new_offset_x + anchor_x, new_offset_y + anchor_y)
+ new_piece.append((new_x, new_y))
+ new_piece = tuple(new_piece)
+ return Piece(self.center, new_piece, self.label)
+
+ def change_center(self, shiftamount):
+ new_piece = []
+ change_x, change_y = shiftamount
+ for x, y in self.positions:
+ new_piece.append((x + change_x, y + change_y))
+ new_piece = tuple(new_piece)
+ return Piece((self.center[0] + change_x, self.center[1] + change_y), new_piece, self.label)
+
+ def bound_ranges(self):
+ max_x = None
+ max_y = None
+ min_x = None
+ min_y = None
+ for x, y in self.positions:
+ if max_x is None or x > max_x:
+ max_x = x
+ if max_y is None or y > max_y:
+ max_y = y
+ if min_x is None or x < min_x:
+ min_x = x
+ if min_y is None or y < min_y:
+ min_y = y
+ return (min_x, max_x), (min_y, max_y)
+
+"Class fot build Tetris Grids, supports clogging with random trash"
+class TetrisGridBuilder:
+ def __init__(self):
+ self.grid = [[]]
+ self.blank = ""
+
+ def set_blank(self, blank):
+ self.blank = blank
+ return self
+
+ def set_grid(self, grid):
+ self.grid = grid
+ return self
+
+ def set_blank_grid(self, width, height, blank):
+ self.set_blank(blank)
+ row = [blank for i in range(width)]
+ grid = [row.copy() for i in range(height)]
+ self.set_grid(grid)
+ return self
+
+ def new_cheese_state(self,
+ x_dimension=10, y_dimension=20, hole_count=1, cheese_count=9, cheese_char="c"):
+ self.set_blank_grid(x_dimension, y_dimension, "_")
+
+ for g in range(y_dimension - cheese_count, y_dimension):
+ to_hole = hole_count
+ cheese_row = [cheese_char] * x_dimension
+
+ while to_hole != 0:
+ hole_index = random.choice(range(x_dimension))
+ cheese_row[hole_index] = self.blank
+ to_hole -= 1
+ self.grid[g] = cheese_row
+ return self
+
+ def build(self):
+ return TetrisGrid(tuple([tuple(row) for row in self.grid]), self.blank)
+
+"Class for Grid"
+class Grid:
+ def __init__(self, grid, blank):
+ self.grid = grid
+ self.blank = blank
+
+ def __hash__(self):
+ return hash((self.grid, self.blank))
+
+ def __eq__(self, other):
+ if isinstance(other, Grid):
+ return (other.blank == self.blank) and (other.grid == self.grid)
+
+ def __repr__(self):
+ out = ""
+ for i in range(len(self.grid)):
+ for col in self.grid[i]:
+ out += str(col)
+ out += " " + str(i) + " \n"
+ return out
+
+def row_is_full(row, blank):
+ for block in row:
+ if block == blank:
+ return False
+ return True
+
+
+def clear_rows(grid, blank):
+ """Clear full rows."""
+ y_dimension = len(grid)
+ x_dimension = len(grid[0])
+ blank_row = tuple([blank] * x_dimension)
+ filtered = list(filter(lambda x: not row_is_full(x, blank), grid))
+ for _ in range(y_dimension - len(filtered)):
+ filtered.insert(0, blank_row)
+ return tuple(filtered)
+
+"Represents the Tetris Grid"
+class TetrisGrid(Grid):
+ #to drop from sky, merely place piece in the clouds
+
+ def hard_drop(self, piece):
+ y_delta = float("inf")
+
+ for x, y in piece.positions:
+ for y_i in range(y, len(self.grid)):
+ if y_i >= 0 and self.grid[y_i][x] != self.blank:
+ y_delta = min(y_delta, y_i - y)
+ break
+ y_delta = min(y_delta, len(self.grid) - y)
+
+ new_piece = piece.change_center((0, y_delta - 1))
+ return self.lock_piece(new_piece)
+
+ def lock_piece(self, piece):
+ newgrid = [[col for col in row] for row in self.grid]
+ for x, y in piece.positions:
+ if 0 <= x < len(self.grid[0]) and 0 <= y < len(self.grid):
+ newgrid[y][x] = piece.label
+ else:
+ return False
+ newgrid = clear_rows(newgrid, self.blank)
+ return TetrisGrid(tuple([tuple(row) for row in newgrid]), self.blank)
+
+ #See if a given thing is contained in the grid
+ def contains(self, symbol):
+ for row in self.grid:
+ for col in row:
+ if col == symbol:
+ return True
+ return False
+
+T_PIECE = Piece((0, 0), ((0, -1), (-1, 0), (0, 0), (1, 0)), "b")
+L_PIECE = Piece((0, 0), ((0, 0), (1, 0), (0, -1), (0, -2)), "b")
+J_PIECE = Piece((0, 0), ((0, 0), (-1, 0), (0, -1), (0, -2)), "b")
+O_PIECE = Piece((0, 0), ((0, 0), (1, 0), (0, -1), (1, -1)), "b")
+S_PIECE = Piece((0, 0), ((0, 0), (-1, 0), (0, -1), (1, -1)), "b")
+Z_PIECE = Piece((0, 0), ((0, 0), (-1, 0), (0, -1), (1, -1)), "b")
+I_PIECE = Piece((0, -1), ((0, 0), (0, -1), (0, -2), (0, -3)), "b")
+
+PIECES = [T_PIECE, L_PIECE, J_PIECE, O_PIECE, S_PIECE, Z_PIECE, I_PIECE]
+
+class CheeseGameLocked:
+ """Clear all the cheese to win."""
+
+ def __init__(self, x_dimension=10, y_dimension=20, hole_count=1, cheese_count=9, pieces=PIECES):
+ self.x_dimension = x_dimension
+ self.y_dimension = y_dimension
+ self.hole_count = hole_count
+ self.cheese_count = cheese_count
+ self.pieces = pieces
+ self.reward = lambda before, after: 0
+
+ def get_start_state(self):
+ return TetrisGridBuilder().new_cheese_state(self.x_dimension, self.y_dimension,
+ self.hole_count, self.cheese_count, "c").build()
+
+ def set_reward(self, reward):
+ self.reward = reward
+
+ def get_successors(self, state):
+ piece = random.choice(self.pieces)
+ successors = []
+ explored = set()
+ for i in range(4):
+ xrange, yrange = piece.bound_ranges()
+ piece = piece.change_center((0 - xrange[0], 0 - yrange[1]))
+ for offset in range(len(state.grid[0]) - xrange[1] + xrange[0]):
+ newstate = state.hard_drop(piece.change_center((offset, 0)))
+ if newstate not in explored:
+ successors.append((newstate, (i, offset), self.reward(state, newstate)))
+ piece = piece.rotate_left()
+
+ return filter(lambda x: x[0] is not False, successors)
+
+ def is_goal(self, state):
+ return not state.contains("c")
\ No newline at end of file