import cv2 as cv
import numpy as np
import random
def __init__(self, width=340, height=340, boardNum = 4):
# 初始化参数
self.width = width
self.height = height
self.cellspace = 10
self.boardNum = boardNum
self.cellw = (width - self.cellspace * (boardNum + 1)) / boardNum
self.cellh = self.cellw
self.score = 0
self.is_game_over = False
# 初始化格子
self.init_board()
# 将16进制颜色转成opencv可以使用BGR颜色值
def Hex_to_BGR(hex):
hex = hex[1:]
r = int(hex[0:2],16)
g = int(hex[2:4],16)
b = int(hex[4:6], 16)
bgr = (b,g,r)
return bgr
# 不同文字对应的背景颜色
def get_board_bg(self, num):
return Hex_to_BGR({
"0": "#cdc1b3",
"2": "#eee4da",
"4": "#eee1c9",
"8": "#f3b27a",
"16": "#f69664",
"32": "#f77c5f",
"64": "#f75f3b",
"128": "#edd073",
"256": "#edcc62",
"512": "#edc950",
"1024": "#edc53f",
"2048": "#edc22e",
"4096": "#eee4da"
}[f'{num}'])
# 不同文本的字体颜色
def get_board_text_color(self, num):
return Hex_to_BGR({
"0": "#cdc1b3",
"2": "#796d65",
"4": "#796d65",
"8": "#ffffff",
"16": "#ffffff",
"32": "#ffffff",
"64": "#ffffff",
"128": "#ffffff",
"256": "#ffffff",
"512": "#ffffff",
"1024": "#ffffff",
"2048": "#ffffff",
"4096": "#ffffff"
}[f'{num}'])
# 初始化2048网格
def init_board(self):
self.board = [[{'x': i, 'y': j, 'num': 0, 'merge': True} for j in range(self.boardNum)] for i in range(self.boardNum)]
使用 numpy 模块将二维列表转一维列表;
# 获取全部格子得一维列表
def get_flat_board(self):
return np.array(self.board).flatten()
生成的随机数小于0.9返回2否则返回4
# 产生随机值2或者4
def get_random(self):
if random.random() < 0.9:
return 2
else:
return 4
# 随机位置填写随机变量
def get_random_board(self):
filters = [item for item in self.get_flat_board() if item["num"] == 0]
filters[random.randint(0,len(filters) - 1)]["num"] = self.get_random()
# 绘制2048UI界面
def draw(self):
self.label.config(text=f'SCPRE\n{self.score}')
for item in self.get_flat_board():
self.draw_rect(item["x"], item["y"], self.cellw, self.cellh, self.get_board_bg(item["num"]))
if(item["num"] != 0):
self.draw_text(item["x"], item["y"], item["num"], self.get_board_text_color(item["num"]))
# 绘制矩形
def draw_rect(self, x, y, width, height, color):
x0 = int(self.cellspace * (x + 1) + self.cellw * x + int((400 - self.width) / 2))
y0 = int(self.cellspace * (y + 1) + self.cellw * y + int((400 - self.height) / 2))
x1 = int(x0 + width)
y1 = int(y0 + height)
cv.rectangle(self.game2048, (x0, y0), (x1, y1), color, -1)
# 绘制文本
def draw_text(self, x, y, text, color):
x = int(self.cellspace * (x + 1) + self.cellw * x + self.cellw / 2 + int((400 - self.width) / 2))
y = int(self.cellspace * (y + 1) + self.cellw * y + self.cellw / 2 + int((400 - self.height) / 2))
(fw,fh),dh = cv.getTextSize(str(text), cv.FONT_HERSHEY_DUPLEX, 1, 1)
x = int(x - fw / 2)
y = int(y + fh / 2)
cv.putText(self.game2048, str(text), (x, y), cv.FONT_HERSHEY_DUPLEX, 1, color,1,cv.LINE_AA)
# 初始化canvas,绘制
def render(self):
self.game2048 = np.zeros((400,400,3),np.uint8)
self.game2048[:] = 255
self.copy_game2048 = np.copy(self.game2048)
x,y = (int((400 - self.width) / 2), int((400 - self.height) / 2))
cv.rectangle(self.game2048, (x,y),(x + self.width, y + self.height),(158, 175, 193),-1)
# 绘制2048UI界面
self.draw()
while True:
cv.imshow("GAME 2048", self.game2048)
key = cv.waitKey(0)
if key == 27:
# ESC退出程序
break
elif key == 119:
# W 向上
self.up()
elif key == 115:
# S 向下
self.down()
elif key == 97:
# A 向左
self.left()
elif key == 100:
# D 向右
self.right()
elif key == 114:
# R 重新开始
self.reset()
else:
pass
cv.destroyAllWindows()
def reset(self):
self.game2048[:] = self.copy_game2048
self.is_game_over = False
self.score = 0
# 初始化格子
self.init_board()
self.draw()
# 上下左右按钮执行事件
def up(self):
if self.is_game_over == False:
self.move("Up")
def down(self):
if self.is_game_over == False:
self.move("Down")
def left(self):
if self.is_game_over == False:
self.move("Left")
def right(self):
if self.is_game_over == False:
self.move("Right")
# 移动元素
def move(self, direction):
filters = [item for item in self.get_flat_board() if item["num"] != 0]
if direction in ["Left","Up"]:
for item in filters:
self.item_move(item, direction)
elif direction in ["Down","Right"]:
for item in list(reversed(filters)):
self.item_move(item, direction)
# 移动完成,设置所有元素允许再次合并
for item in self.get_flat_board():
item["merge"] = True
# 判断游戏是否结束
self.has_game_over()
if self.is_game_over == False:
# 生成随机数
self.get_random_board()
# 移动完成,生成随机数完成,绘制新的矩阵
self.draw()
else:
self.draw()
# 生成游戏结束界面
self.draw_game_over()
# 单个元素的移动
def item_move(self, item, direction):
item_side = self.get_current_item_side(item, direction)
if item_side == False:
# 边界不操作
pass
elif item_side["num"] == 0:
# 说明移动方向旁边位置为空,将其移动到旁边位置
item_side["num"] = item["num"]
item["num"] = 0
if item["merge"] == False:
item_side["merge"] = False
item["merge"] = True
self.item_move(item_side, direction)
elif item_side["num"] != item["num"]:
# 说明当前和旁边元素不同,不进行移动
pass
elif item_side["num"] == item["num"] and item_side["merge"] == True and item["merge"] == True:
# 说明当前和旁边元素相同,将当前数给旁边元素,当前元素置为0
item_side["num"] = item_side["num"] * 2
self.score += item_side["num"]
item["num"] = 0
item_side["merge"] = False
item["merge"] = True
else:
return
# 获取当前元素旁边元素的值
def get_current_item_side(self, item, direction):
x = item["x"]
y = item["y"]
if direction in ["Left"]:
x = x - 1
if direction in ["Right"]:
x = x + 1
if direction in ["Up"]:
y = y - 1
if direction in ["Down"]:
y = y + 1
if x >= 0 and x < self.boardNum and y >= 0 and y < self.boardNum:
return self.board[x][y]
else:
return False
# 是否游戏结束
def has_game_over(self):
filters = self.get_flat_board()
if 2048 in [item["num"] for item in filters]:
self.is_game_over = True
elif len([item["num"] for item in filters if item["num"] > 0]) == len(filters):
for item in filters:
item_left = self.get_current_item_side(item, 'Left')
item_right = self.get_current_item_side(item, 'Right')
item_up = self.get_current_item_side(item, 'Up')
item_down = self.get_current_item_side(item, 'Down')
if item_left != False and item_left["num"] == item["num"]:
return
elif item_right != False and item_right["num"] == item["num"]:
return
elif item_up != False and item_up["num"] == item["num"]:
return
elif item_down != False and item_down["num"] == item["num"]:
return
else:
return
self.is_game_over = True
import cv2 as cv
import numpy as np
import random
class G2048():
def __init__(self, width=340, height=340, boardNum = 4):
# 初始化参数
self.width = width
self.height = height
self.cellspace = 10
self.boardNum = boardNum
self.cellw = (width - self.cellspace * (boardNum + 1)) / boardNum
self.cellh = self.cellw
self.score = 0
self.is_game_over = False
# 初始化格子
self.init_board()
# 不同文字对应的背景颜色
def get_board_bg(self, num):
return Hex_to_BGR({
"0": "#cdc1b3",
"2": "#eee4da",
"4": "#eee1c9",
"8": "#f3b27a",
"16": "#f69664",
"32": "#f77c5f",
"64": "#f75f3b",
"128": "#edd073",
"256": "#edcc62",
"512": "#edc950",
"1024": "#edc53f",
"2048": "#edc22e",
"4096": "#eee4da"
}[f'{num}'])
# 不同文本的字体颜色
def get_board_text_color(self, num):
return Hex_to_BGR({
"0": "#cdc1b3",
"2": "#796d65",
"4": "#796d65",
"8": "#ffffff",
"16": "#ffffff",
"32": "#ffffff",
"64": "#ffffff",
"128": "#ffffff",
"256": "#ffffff",
"512": "#ffffff",
"1024": "#ffffff",
"2048": "#ffffff",
"4096": "#ffffff"
}[f'{num}'])
# 初始化2048网格
def init_board(self):
self.board = [[{'x': i, 'y': j, 'num': 0, 'merge': True} for j in range(self.boardNum)] for i in range(self.boardNum)]
# 生成随机位置的随机数
self.get_random_board()
self.get_random_board()
# 获取全部格子得一维列表
def get_flat_board(self):
return np.array(self.board).flatten()
# 产生随机值2或者4
def get_random(self):
if random.random() < 0.9:
return 2
else:
return 4
# 随机位置填写随机变量
def get_random_board(self):
filters = [item for item in self.get_flat_board() if item["num"] == 0]
filters[random.randint(0,len(filters) - 1)]["num"] = self.get_random()
# 绘制2048UI界面
def draw(self):
for item in self.get_flat_board():
self.draw_rect(item["x"], item["y"], self.cellw, self.cellh, self.get_board_bg(item["num"]))
if(item["num"] != 0):
self.draw_text(item["x"], item["y"], item["num"], self.get_board_text_color(item["num"]))
# 绘制矩形
def draw_rect(self, x, y, width, height, color):
x0 = int(self.cellspace * (x + 1) + self.cellw * x + int((400 - self.width) / 2))
y0 = int(self.cellspace * (y + 1) + self.cellw * y + int((400 - self.height) / 2))
x1 = int(x0 + width)
y1 = int(y0 + height)
cv.rectangle(self.game2048, (x0, y0), (x1, y1), color, -1)
# 绘制文本
def draw_text(self, x, y, text, color):
x = int(self.cellspace * (x + 1) + self.cellw * x + self.cellw / 2 + int((400 - self.width) / 2))
y = int(self.cellspace * (y + 1) + self.cellw * y + self.cellw / 2 + int((400 - self.height) / 2))
(fw,fh),dh = cv.getTextSize(str(text), cv.FONT_HERSHEY_DUPLEX, 1, 1)
x = int(x - fw / 2)
y = int(y + fh / 2)
cv.putText(self.game2048, str(text), (x, y), cv.FONT_HERSHEY_DUPLEX, 1, color,1,cv.LINE_AA)
def reset(self):
self.game2048[:] = self.copy_game2048
self.is_game_over = False
self.score = 0
# 初始化格子
self.init_board()
self.draw()
# 上下左右按钮执行事件
def up(self):
if self.is_game_over == False:
self.move("Up")
def down(self):
if self.is_game_over == False:
self.move("Down")
def left(self):
if self.is_game_over == False:
self.move("Left")
def right(self):
if self.is_game_over == False:
self.move("Right")
# 初始化canvas,绘制
def render(self):
self.game2048 = np.zeros((400,400,3),np.uint8)
self.game2048[:] = 255
self.copy_game2048 = np.copy(self.game2048)
x,y = (int((400 - self.width) / 2), int((400 - self.height) / 2))
cv.rectangle(self.game2048, (x,y),(x + self.width, y + self.height),(158, 175, 193),-1)
# 绘制2048UI界面
self.draw()
while True:
cv.imshow("GAME 2048", self.game2048)
key = cv.waitKey(0)
if key == 27:
# ESC退出程序
break
elif key == 119:
# W 向上
self.up()
elif key == 115:
# S 向下
self.down()
elif key == 97:
# A 向左
self.left()
elif key == 100:
# D 向右
self.right()
elif key == 114:
# R 重新开始
self.reset()
else:
pass
cv.destroyAllWindows()
# 单个元素的移动
def item_move(self, item, direction):
item_side = self.get_current_item_side(item, direction)
if item_side == False:
# 边界不操作
pass
elif item_side["num"] == 0:
# 说明移动方向旁边位置为空,将其移动到旁边位置
item_side["num"] = item["num"]
item["num"] = 0
if item["merge"] == False:
item_side["merge"] = False
item["merge"] = True
self.item_move(item_side, direction)
elif item_side["num"] != item["num"]:
# 说明当前和旁边元素不同,不进行移动
pass
elif item_side["num"] == item["num"] and item_side["merge"] == True and item["merge"] == True:
# 说明当前和旁边元素相同,将当前数给旁边元素,当前元素置为0
item_side["num"] = item_side["num"] * 2
self.score += item_side["num"]
item["num"] = 0
item_side["merge"] = False
item["merge"] = True
else:
return
# 获取当前元素旁边元素的值
def get_current_item_side(self, item, direction):
x = item["x"]
y = item["y"]
if direction in ["Left"]:
x = x - 1
if direction in ["Right"]:
x = x + 1
if direction in ["Up"]:
y = y - 1
if direction in ["Down"]:
y = y + 1
if x >= 0 and x < self.boardNum and y >= 0 and y < self.boardNum:
return self.board[x][y]
else:
return False
# 移动元素
def move(self, direction):
filters = [item for item in self.get_flat_board() if item["num"] != 0]
if direction in ["Left","Up"]:
for item in filters:
self.item_move(item, direction)
elif direction in ["Down","Right"]:
for item in list(reversed(filters)):
self.item_move(item, direction)
# 移动完成,设置所有元素允许再次合并
for item in self.get_flat_board():
item["merge"] = True
# 判断游戏是否结束
self.has_game_over()
if self.is_game_over == False:
# 生成随机数
self.get_random_board()
# 移动完成,生成随机数完成,绘制新的矩阵
self.draw()
else:
self.draw()
# 生成游戏结束界面
self.draw_game_over()
# 生成游戏结束界面
def draw_game_over(self):
(fw,fh),dh = cv.getTextSize("Game Over!", cv.FONT_HERSHEY_DUPLEX, 1.2, 1)
x = int(200 - fw / 2)
y = int(200 + fh / 2)
cv.putText(self.game2048, "Game Over!", (x, y), cv.FONT_HERSHEY_DUPLEX, 1.2, (255,255,255),1,cv.LINE_AA)
# 是否游戏结束
def has_game_over(self):
filters = self.get_flat_board()
if 2048 in [item["num"] for item in filters]:
self.is_game_over = True
elif len([item["num"] for item in filters if item["num"] > 0]) == len(filters):
for item in filters:
item_left = self.get_current_item_side(item, 'Left')
item_right = self.get_current_item_side(item, 'Right')
item_up = self.get_current_item_side(item, 'Up')
item_down = self.get_current_item_side(item, 'Down')
if item_left != False and item_left["num"] == item["num"]:
return
elif item_right != False and item_right["num"] == item["num"]:
return
elif item_up != False and item_up["num"] == item["num"]:
return
elif item_down != False and item_down["num"] == item["num"]:
return
else:
return
self.is_game_over = True
# 将16进制颜色转成opencv可以使用BGR颜色值
def Hex_to_BGR(hex):
hex = hex[1:]
r = int(hex[0:2],16)
g = int(hex[2:4],16)
b = int(hex[4:6], 16)
bgr = (b,g,r)
return bgr
if __name__ == '__main__':
g2048 = G2048()
g2048.render()