You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
327 lines
11 KiB
327 lines
11 KiB
6 months ago
|
import copy
|
||
|
import random
|
||
|
import tkinter
|
||
|
import tkinter.messagebox
|
||
|
from PIL import ImageTk, Image
|
||
|
|
||
|
|
||
|
class Matrix2048():
|
||
|
def __init__(self, column: int = 4):
|
||
|
self.column = column
|
||
|
self.matrix = [[0 for i in range(column)] for li in range(column)]
|
||
|
self.history = []
|
||
|
self.score = 0
|
||
|
self.init()
|
||
|
|
||
|
def generate_number(self):
|
||
|
matrix = self.matrix
|
||
|
column = self.column
|
||
|
zero = [(x, y) for x in range(column)
|
||
|
for y in range(column) if matrix[x][y] == 0]
|
||
|
if zero != []:
|
||
|
x, y = random.choice(zero)
|
||
|
matrix[x][y] = random.choice([2, 2])
|
||
|
|
||
|
def gameover(self) -> bool:
|
||
|
matrix = self.matrix
|
||
|
column = self.column
|
||
|
|
||
|
if 0 in [i for li in matrix for i in li]:
|
||
|
return False
|
||
|
|
||
|
|
||
|
for row in range(column):
|
||
|
for col in range(column-1):
|
||
|
if matrix[row][col] == matrix[row][col+1]:
|
||
|
return False
|
||
|
|
||
|
|
||
|
for row in range(column-1):
|
||
|
for col in range(column):
|
||
|
if matrix[row][col] == matrix[row+1][col]:
|
||
|
return False
|
||
|
return True
|
||
|
|
||
|
|
||
|
def init(self):
|
||
|
self.matrix = [[0 for x in range(self.column)]
|
||
|
for y in range(self.column)]
|
||
|
self.generate_number()
|
||
|
self.generate_number()
|
||
|
|
||
|
self.history = []
|
||
|
self.score = 0
|
||
|
|
||
|
|
||
|
def matrix_move(self, direction):
|
||
|
if direction in ['L', 'R', 'D', 'U']:
|
||
|
|
||
|
|
||
|
prev_step = {
|
||
|
'score': copy.deepcopy(self.score),
|
||
|
'matrix': copy.deepcopy(self.matrix)
|
||
|
}
|
||
|
self.history.append(prev_step)
|
||
|
|
||
|
if len(self.history) > 10:
|
||
|
self.history = self.history[-10:]
|
||
|
if direction == 'U':
|
||
|
self.move_up()
|
||
|
if direction == 'D':
|
||
|
self.move_down()
|
||
|
if direction == 'L':
|
||
|
self.move_left()
|
||
|
if direction == 'R':
|
||
|
self.move_right()
|
||
|
|
||
|
|
||
|
def move_left(self):
|
||
|
column = self.column
|
||
|
matrix = self.matrix
|
||
|
|
||
|
|
||
|
def move_left_(matrix):
|
||
|
for row in range(column):
|
||
|
while 0 in matrix[row]:
|
||
|
matrix[row].remove(0)
|
||
|
while len(matrix[row]) != column:
|
||
|
matrix[row].append(0)
|
||
|
return matrix
|
||
|
|
||
|
|
||
|
def merge_left(matrix):
|
||
|
for row in range(column):
|
||
|
for col in range(column-1):
|
||
|
if matrix[row][col] == matrix[row][col+1] and matrix[row][col] != 0:
|
||
|
matrix[row][col] = 2 * matrix[row][col]
|
||
|
matrix[row][col+1] = 0
|
||
|
self.score = self.score + matrix[row][col]
|
||
|
return matrix
|
||
|
|
||
|
matrix = move_left_(matrix)
|
||
|
matrix = merge_left(matrix)
|
||
|
self.matrix = move_left_(matrix)
|
||
|
|
||
|
|
||
|
def move_right(self):
|
||
|
self.matrix = [li[::-1] for li in self.matrix]
|
||
|
self.move_left()
|
||
|
self.matrix = [li[::-1] for li in self.matrix]
|
||
|
|
||
|
def move_up(self):
|
||
|
column = self.column
|
||
|
|
||
|
self.matrix = [[self.matrix[x][y]
|
||
|
for x in range(column)] for y in range(column)]
|
||
|
self.move_left()
|
||
|
self.matrix = [[self.matrix[x][y]
|
||
|
for x in range(column)] for y in range(column)]
|
||
|
|
||
|
def move_down(self):
|
||
|
self.matrix = self.matrix[::-1]
|
||
|
self.move_up()
|
||
|
self.matrix = self.matrix[::-1]
|
||
|
|
||
|
def prev_step(self):
|
||
|
if self.history:
|
||
|
prev_data = self.history[-1]
|
||
|
self.score = prev_data['score']
|
||
|
self.matrix = prev_data['matrix']
|
||
|
self.history = self.history[0:-1]
|
||
|
|
||
|
def show(self):
|
||
|
r = '+-----' * self.column + '+\n'
|
||
|
for li in self.matrix:
|
||
|
for i in li:
|
||
|
s = '|' + ' '*5 if i == 0 else '|' + str(i).center(5, ' ')
|
||
|
r = r + s
|
||
|
r = r + '|\n' + '+-----' * self.column + '+\n'
|
||
|
print(r)
|
||
|
|
||
|
|
||
|
class Window2048():
|
||
|
|
||
|
def __init__(self, column: int = 4):
|
||
|
self.init_setting(column)
|
||
|
self.data = Matrix2048(column)
|
||
|
self.root = self.init_root()
|
||
|
self.t = 0 # 判断游戏结束时用
|
||
|
self.main()
|
||
|
|
||
|
def init_setting(self, column):
|
||
|
|
||
|
self.column = column
|
||
|
|
||
|
self.space_size = 12
|
||
|
|
||
|
self.cell_size = 80
|
||
|
|
||
|
self.emts = [] # 存储lable对象
|
||
|
|
||
|
self.style = {
|
||
|
'page': {'bg': '#d6dee0', },
|
||
|
0: {'bg': '#EEEEEE', 'fg': '#EEEEEE', 'fz': 30},
|
||
|
2**1: {'bg': '#E5E5E5', 'fg': '#707070', 'fz': 30},
|
||
|
2**2: {'bg': '#D4D4D4', 'fg': '#707070', 'fz': 30},
|
||
|
2**3: {'bg': '#FFCC80', 'fg': '#FAFAFA', 'fz': 30},
|
||
|
2**4: {'bg': '#FFB74D', 'fg': '#FAFAFA', 'fz': 30},
|
||
|
2**5: {'bg': '#FF7043', 'fg': '#FAFAFA', 'fz': 30},
|
||
|
2**6: {'bg': '#FF5722', 'fg': '#FAFAFA', 'fz': 30},
|
||
|
2**7: {'bg': '#FFEE58', 'fg': '#FAFAFA', 'fz': 30},
|
||
|
2**8: {'bg': '#FFEB3B', 'fg': '#FAFAFA', 'fz': 30},
|
||
|
2**9: {'bg': '#FDD835', 'fg': '#FAFAFA', 'fz': 30},
|
||
|
2**10: {'bg': '#FF9800', 'fg': '#FAFAFA', 'fz': 30},
|
||
|
2**11: {'bg': '#FB8C00', 'fg': '#FAFAFA', 'fz': 28},
|
||
|
2**12: {'bg': '#fb3030', 'fg': '#FAFAFA', 'fz': 28},
|
||
|
2**13: {'bg': '#e92e2e', 'fg': '#FAFAFA', 'fz': 28},
|
||
|
2**14: {'bg': '#da1e1e', 'fg': '#FAFAFA', 'fz': 24},
|
||
|
2**15: {'bg': '#3a3a3a', 'fg': '#E0E0E0', 'fz': 22},
|
||
|
2**16: {'bg': '#3a3a3a', 'fg': '#E0E0E0', 'fz': 20},
|
||
|
2**17: {'bg': '#3a3a3a', 'fg': '#E0E0E0', 'fz': 20},
|
||
|
2**18: {'bg': '#3a3a3a', 'fg': '#E0E0E0', 'fz': 20},
|
||
|
2**19: {'bg': '#3a3a3a', 'fg': '#E0E0E0', 'fz': 18},
|
||
|
2**20: {'bg': '#3a3a3a', 'fg': '#E0E0E0', 'fz': 17},
|
||
|
2**21: {'bg': '#3a3a3a', 'fg': '#E0E0E0', 'fz': 16},
|
||
|
2**22: {'bg': '#3a3a3a', 'fg': '#E0E0E0', 'fz': 15},
|
||
|
2**23: {'bg': '#3a3a3a', 'fg': '#E0E0E0', 'fz': 14},
|
||
|
2**24: {'bg': '#3a3a3a', 'fg': '#E0E0E0', 'fz': 13},
|
||
|
2**25: {'bg': '#3a3a3a', 'fg': '#E0E0E0', 'fz': 12},
|
||
|
}
|
||
|
|
||
|
def init_root(self):
|
||
|
column = self.column
|
||
|
space_size = self.space_size
|
||
|
cell_size = self.cell_size
|
||
|
|
||
|
root = tkinter.Tk()
|
||
|
root.title('2048')
|
||
|
|
||
|
window_w = column * (space_size + cell_size) + space_size
|
||
|
window_h = window_w + cell_size + 2 * space_size
|
||
|
root.geometry(f'{window_w}x{window_h}')
|
||
|
|
||
|
header_h = cell_size + space_size * 2
|
||
|
header = tkinter.Frame(root, height=header_h, width=window_w)
|
||
|
self.init_header(header)
|
||
|
|
||
|
table = tkinter.Frame(root, height=window_w, width=window_w)
|
||
|
self.init_table(table)
|
||
|
|
||
|
return root
|
||
|
|
||
|
def init_header(self, master):
|
||
|
master['bg'] = self.style['page']['bg']
|
||
|
|
||
|
emt_score = tkinter.Label(master, bd=0)
|
||
|
emt_score['fg'] = '#707070'
|
||
|
emt_score['bg'] = self.style['page']['bg']
|
||
|
emt_score['font'] = ("黑体", 30, "bold")
|
||
|
img = Image.new('RGB', (self.cell_size, self.cell_size),
|
||
|
self.style['page']['bg'])
|
||
|
img = ImageTk.PhotoImage(img)
|
||
|
emt_score.configure(image=img)
|
||
|
emt_score['image'] = img
|
||
|
|
||
|
emt_score['text'] = 'score:' + str(self.data.score)
|
||
|
emt_score['compound'] = 'center'
|
||
|
self.emt_score = emt_score
|
||
|
emt_score.place(x=15, y=15)
|
||
|
master.pack()
|
||
|
|
||
|
def init_table(self, master):
|
||
|
column = self.column
|
||
|
cell_size = self.cell_size
|
||
|
space_size = self.space_size
|
||
|
|
||
|
master['bg'] = self.style['page']['bg']
|
||
|
|
||
|
emts = [[0 for x in range(column)] for y in range(column)]
|
||
|
for row in range(column):
|
||
|
for col in range(column):
|
||
|
emt = tkinter.Label(master, bd=0)
|
||
|
emt['width'] = self.cell_size
|
||
|
emt['height'] = self.cell_size
|
||
|
emt['text'] = ''
|
||
|
emt['compound'] = 'center'
|
||
|
|
||
|
y = space_size + (cell_size + space_size) * row
|
||
|
x = space_size + (cell_size + space_size) * col
|
||
|
|
||
|
emt.place(x=x, y=y)
|
||
|
emts[row][col] = emt
|
||
|
self.emts = emts
|
||
|
master.pack()
|
||
|
|
||
|
def update_ui(self):
|
||
|
def update_score():
|
||
|
img = Image.new(
|
||
|
'RGB', (self.cell_size, self.cell_size), self.style['page']['bg'])
|
||
|
img = ImageTk.PhotoImage(img)
|
||
|
self.emt_score.configure(image=img)
|
||
|
self.emt_score['image'] = img
|
||
|
|
||
|
self.emt_score['text'] = 'score:' + str(self.data.score)
|
||
|
update_score()
|
||
|
matrix = self.data.matrix
|
||
|
for row in range(self.column):
|
||
|
for col in range(self.column):
|
||
|
num = matrix[row][col]
|
||
|
emt = self.emts[row][col]
|
||
|
img = Image.new(
|
||
|
'RGB', (self.cell_size, self.cell_size), self.style[num]['bg'])
|
||
|
img = ImageTk.PhotoImage(img)
|
||
|
emt.configure(image=img)
|
||
|
emt['fg'] = self.style[num]['fg']
|
||
|
emt['bg'] = self.style[num]['bg']
|
||
|
emt['image'] = img
|
||
|
emt['font'] = ("黑体", self.style[num]['fz'], "bold")
|
||
|
emt['text'] = str(num) if num != 0 else ''
|
||
|
|
||
|
def key_event(self, event):
|
||
|
if event.keysym in ['Up', 'w', 'Down', 's', 'Left', 'a', 'Right', 'd']:
|
||
|
if event.keysym in ['Up', 'w']: # 向上
|
||
|
self.data.matrix_move('U')
|
||
|
elif event.keysym in ['Down', 's']: # 向下
|
||
|
self.data.matrix_move('D')
|
||
|
elif event.keysym in ['Left', 'a']: # 向左
|
||
|
self.data.matrix_move('L')
|
||
|
elif event.keysym in ['Right', 'd']: # 向右
|
||
|
self.data.matrix_move('R')
|
||
|
self.data.generate_number()
|
||
|
|
||
|
if event.keysym == 'z':
|
||
|
if self.data.history != []:
|
||
|
self.data.prev_step()
|
||
|
self.update_ui()
|
||
|
|
||
|
def reset_game():
|
||
|
self.t = 0
|
||
|
self.data.init()
|
||
|
self.update_ui()
|
||
|
|
||
|
if self.data.gameover() is True:
|
||
|
|
||
|
if self.t == 0:
|
||
|
self.t = 1
|
||
|
else:
|
||
|
res = tkinter.messagebox.askyesno(
|
||
|
title="2048", message="Game Over!\n是否重新开始!")
|
||
|
if res is True:
|
||
|
reset_game()
|
||
|
else:
|
||
|
self.root.quit()
|
||
|
|
||
|
def reset_game(self):
|
||
|
self.t = 0
|
||
|
self.data.init()
|
||
|
self.update_ui()
|
||
|
|
||
|
def main(self):
|
||
|
self.update_ui()
|
||
|
|
||
|
self.root.bind('<Key>', self.key_event)
|
||
|
|
||
|
self.root.mainloop()
|
||
|
|
||
|
|
||
|
g = Window2048(4)
|