import cv2 as cv import numpy as np from game_base_class import GameBase from pynput.keyboard import Key, Controller from field import Field from tetromino import Tetromino from optimizer import Optimizer from litris_stone_id_thread import NewStoneID O_FULL = [[0, 0, 0, 0], [0, 1, 1, 0], [0, 1, 1, 0], [0, 0, 0, 0]] D_FULL = [[0, 0, 0, 0], [0, 1, 1, 0], [0, 0, 1, 0], [0, 0, 0, 0]] L_FULL = [[0, 0, 0, 0], [0, 0, 1, 0], [1, 1, 1, 0], [0, 0, 0, 0]] J_FULL = [[0, 0, 0, 0], [1, 1, 1, 0], [0, 0, 1, 0], [0, 0, 0, 0]] I_FULL = [[0, 0, 0, 0], [1, 1, 1, 1], [0, 0, 0, 0], [0, 0, 0, 0]] C_FULL = [[0, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 0], [0, 0, 0, 0]] B_FULL = [[0, 0, 0, 0], [0, 1, 1, 0], [0, 0, 0, 0], [0, 0, 0, 0]] A_FULL = [[0, 0, 0, 0], [0, 1, 1, 1], [0, 0, 0, 0], [0, 0, 0, 0]] S_FULL = [[0, 0, 0, 0], [0, 0, 1, 1], [0, 1, 1, 0], [0, 0, 0, 0]] Z_FULL = [[0, 0, 0, 0], [0, 1, 1, 0], [0, 0, 1, 1], [0, 0, 0, 0]] T_FULL = [[0, 0, 0, 0], [0, 1, 1, 1], [0, 0, 1, 0], [0, 0, 0, 0]] class Litris(GameBase): def __init__(self, overlay): super().__init__(overlay) self.keyboard = Controller() self.data_coordinates = np.zeros((20, 20), dtype=object) self.stone_coordinates = np.zeros((4, 4), dtype=object) self.observation = np.zeros((20, 20), dtype=int) self.colors = [1, 2, 3, 4, 5, 6, 7, 8, 9] self.offset_left = 610 self.offset_down = 40 self.fill_data_coordinates() self.field = Field() #self.sd_reset_board = cv.imread("control_elements/sodoku_reset_button.jpg", cv.IMREAD_COLOR) self.needles = {1: cv.imread("litris/blue_needle.jpg", cv.IMREAD_UNCHANGED)} self.stone_id_thread = NewStoneID() def reset_field(self): self.state = [[' ' for cols in range(Field.WIDTH)] for rows in range(Field.HEIGHT)] def fill_data_coordinates(self): # 610 to 1950 = 1340 - 76 / 20 = 63 # 40 to 1380 = 1340 - 76 / 20 = 63 # spacing 19 * 4 dim = 63 e_spacing = 4 i_spacing = 4 for e in range(0, 20, 1): for i in range(0, 20, 1): self.data_coordinates[e][i] = [(i * dim) + (i * i_spacing), (e * dim) + (e * e_spacing), dim, dim] for e in range(0, 4, 1): for i in range(0, 4, 1): self.stone_coordinates[e][i] = [(i * dim) + (i * i_spacing), (e * dim) + (e * e_spacing), dim, dim] def assess_playfield_and_make_move(self): while True: if self.stone_id_thread.get_pick_up_status() == False: if self.overlay.run_mode == 'stopped' or self.overlay.run_mode == 'paused': return cv.waitKey(50) continue #current_letter = self.new_stone_detection_and_identification() #current_letter = self.stone_id() #current_letter = 'D' current_letter = self.stone_id_thread.get_actual_letter() #self.stone_id_thread.set_pick_up_status(False) print("current_letter: ", current_letter) #if current_letter is None: # cv.waitKey(50) # return current_tetromino = Tetromino.create(current_letter) opt = Optimizer.get_optimal_drop(self.field, current_tetromino) rotation = opt['tetromino_rotation'] column = opt['tetromino_column'] print("Rota:", rotation) print("column:", column) current_tetromino.rotate(rotation) offset_col = current_tetromino.get_offset_column(rotation) print("offset column:", offset_col) self.field.drop(current_tetromino, column) #self.move_stone(column - offset_col, rotation) for i in range(1,5,1): self.keyboard.press(Key.down) self.keyboard.release(Key.down) print("direction pressed: drop down") cv.waitKey(50) self.stone_id_thread.set_pick_up_status(False) print(self.field) def get_current_board_state(self): # get an updated image of the game screenshot = self.capture_window.get_screenshot() #screenshot = cv.imread("litris/main_playfield.jpg") # gray = cv.cvtColor(screenshot, cv.COLOR_BGR2GRAY) # thresh = cv.threshold(gray, 0, 255, cv.THRESH_BINARY_INV + cv.THRESH_OTSU)[1] #if self.check_for_button_and_execute(screenshot, self.ok_button): # cv.waitKey(500) #screenshot = self.capture_window.get_screenshot() screenshot = screenshot[40:1380, 610:1950] #cv.imshow("screenshot", screenshot) #cv.waitKey(150) #continue data_coords = np.zeros((20, 20), dtype=object) # field = Pickaxe_Field() for needle_key in self.needles.keys(): # gray_needle = cv.cvtColor(self.needles[needle_key], cv.COLOR_BGR2GRAY) # thresh_needle = cv.threshold(gray_needle, 0, 255, cv.THRESH_BINARY_INV + cv.THRESH_OTSU)[1] rectangles = self.vision_stun.find(screenshot, self.needles[needle_key], 0.85, 56) if len(rectangles) == 0: continue points = self.vision_stun.get_click_points(rectangles) for point in points: x, y = self.point_in_rect(point) if x is not None and y is not None: data_coords[x][y] = int(needle_key) # self.change_value(x, y, int(needle_key)) # print(field.data_value_grid) #cv.circle(screenshot, points[0], 7, (0, 255, 0), -1) #output_image = self.vision_stun.draw_rectangles(screenshot, rectangles) #cv.imshow("output_image", output_image) #cv.waitKey(150) return data_coords, screenshot def stone_id(self): fail_counter = 0 while True: screenshot = self.capture_window.get_screenshot() #screenshot = cv.imread("litris/main_playfield.jpg") # 1148 1412 580 845 screenshot = screenshot[623:802, 1128:1432] #cv.imshow("screenshot", screenshot) #cv.waitKey(150) for needle_key in self.needles.keys(): #rectangles = self.vision_stun.find(screenshot, self.needles[needle_key], 0.85, 1) rectangles = self.vision_stun.find(screenshot, self.needles[needle_key], 0.85, 1, False, self.needle_masks[needle_key]) if len(rectangles) == 0: if self.overlay.run_mode == 'stopped' or self.overlay.run_mode == 'paused': return continue else: return needle_key if self.overlay.run_mode == 'stopped' or self.overlay.run_mode == 'paused': return if fail_counter >= 3: self.keyboard.press(Key.down) self.keyboard.release(Key.down) print("direction pressed: drop down") cv.waitKey(50) fail_counter = fail_counter + 1 cv.waitKey(50) def new_stone_detection_and_identification(self): stone_coords = np.zeros((4, 4), dtype=object) fail_counter = 0 while True: screenshot = self.capture_window.get_screenshot() #screenshot = cv.imread("litris/main_playfield.jpg") # 1148 1412 580 845 screenshot = screenshot[580:845, 1148:1412] #cv.imshow("screenshot", screenshot) #cv.waitKey(150) # gray_needle = cv.cvtColor(self.needles[needle_key], cv.COLOR_BGR2GRAY) # thresh_needle = cv.threshold(gray_needle, 0, 255, cv.THRESH_BINARY_INV + cv.THRESH_OTSU)[1] rectangles = self.vision_stun.find(screenshot, self.needles[1], 0.85, 16) if len(rectangles) == 0: if self.overlay.run_mode == 'stopped' or self.overlay.run_mode == 'paused': return if fail_counter >= 5: self.keyboard.press(Key.down) self.keyboard.release(Key.down) print("direction pressed: drop down") cv.waitKey(50) fail_counter = fail_counter + 1 cv.waitKey(50) continue points = self.vision_stun.get_click_points(rectangles) for point in points: x, y = self.point_in_smal_rect(point) if x is not None and y is not None: stone_coords[x][y] = 1 # self.change_value(x, y, int(needle_key)) # print(field.data_value_grid) #cv.circle(screenshot, points[0], 7, (0, 255, 0), -1) #output_image = self.vision_stun.draw_rectangles(screenshot, rectangles) #cv.imshow("output_image", output_image) #cv.waitKey(150) current_letter = self.get_letter_for_stone(stone_coords) if current_letter is None: print("letter id failed for: ", stone_coords) cv.waitKey(50) continue return current_letter def get_letter_for_stone(self, stone): if np.array_equal(stone, O_FULL): return "O" elif np.array_equal(stone, D_FULL): return "D" elif np.array_equal(stone, L_FULL): return "L" elif np.array_equal(stone, J_FULL): return "J" elif np.array_equal(stone, I_FULL): return "I" elif np.array_equal(stone, C_FULL): return "C" elif np.array_equal(stone, B_FULL): return "B" elif np.array_equal(stone, A_FULL): return "A" elif np.array_equal(stone, S_FULL): return "S" elif np.array_equal(stone, Z_FULL): return "Z" elif np.array_equal(stone, T_FULL): return "T" else: return None def move_stone(self, col_movement, rotation): if col_movement is None: return # Press and release space self.keyboard.press(Key.down) self.keyboard.release(Key.down) print("direction pressed: down") cv.waitKey(120) if rotation == 3: self.keyboard.press('e') self.keyboard.release('e') print("rotation 1 pressed: e") cv.waitKey(40) elif rotation == 2: self.keyboard.press('e') self.keyboard.release('e') print("rotation 2 pressed: e 1") cv.waitKey(30) self.keyboard.press('e') self.keyboard.release('e') print("rotation 2 pressed: e 2") cv.waitKey(30) elif rotation == 1: self.keyboard.press('e') self.keyboard.release('e') print("rotation 3 pressed: e 1") cv.waitKey(20) self.keyboard.press('e') self.keyboard.release('e') print("rotation 3 pressed: e 2") cv.waitKey(20) self.keyboard.press('e') self.keyboard.release('e') print("rotation 3 pressed: e 3") cv.waitKey(20) if col_movement < 0: for i in range(0, col_movement, - 1): self.keyboard.press(Key.left) self.keyboard.release(Key.left) print("move left 3 pressed") cv.waitKey(40) else: for i in range(0, col_movement, 1): self.keyboard.press(Key.right) self.keyboard.release(Key.right) print("move right 3 pressed") cv.waitKey(40) def point_in_rect(self, point): for e in range(0, 20, 1): for i in range(0, 20, 1): x1, y1, w, h = self.data_coordinates[e][i] x2, y2 = x1 + w, y1 + h x, y = point if x1 < x and x < x2: if y1 < y and y < y2: return e, i return None, None def point_in_smal_rect(self, point): for e in range(0, 4, 1): for i in range(0, 4, 1): x1, y1, w, h = self.stone_coordinates[e][i] x2, y2 = x1 + w, y1 + h x, y = point if x1 < x and x < x2: if y1 < y and y < y2: return e, i return None, None