这篇文章是 CUHK(SZ) CSC1002 23 spring课程assignment的代码 环境:(python 3.10以上)

assignment 1 (得分:93/100):

这是数字华容道,分"9与16两个版本"

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
from random import random

def is_same(a : list, b : list, mode : int) -> bool:
"""
find out wether two board are same
"""
for i in range(mode+2):
for j in range(mode+2):
if(a[i][j] != b[i][j]):
return False
return True

def limt_rand(lst : list) -> int:
"""
this function choose the number from a to b-1 randomly that is in list
"""
rand_variale = 0 #choose a random real number
ans = 0
rand_variale = int(random()*len(lst)) #choose a random real number in [0,len(lst))
ans = lst[rand_variale]
lst.remove(ans) #del what we choose to avoid redundancy
return ans

def print_board(board : list, mode : int) -> None:
"""
print the board for 3*3 (mode 1) and 4*4 (mode 2) puzzle
"""
value = 0 #value on the board
print()
for i in range(mode+2):
for j in range(mode+2):
value = board[i][j] #the value we need to print
if(value != 0):
if(value < 10):
print('0', value, end = ' ', sep = '')
else:
print(value, end = ' ')
else:
print(' ', end = ' ')
print()
print()
return

def movement(zero_position : tuple[int], board : list, type : str) -> tuple[int]:
"""
the movement if you have determined what situation you need
"""
zero = board[zero_position[0]][zero_position[1]] #value of zero
tile1 = 0 #tile that the zero move to
if(type == 'l'):
tile1 = board[zero_position[0]][zero_position[1] - 1]
board[zero_position[0]][zero_position[1] - 1] = zero
board[zero_position[0]][zero_position[1]] = tile1
return (zero_position[0], zero_position[1] - 1)
elif(type == 'r'):
tile1 = board[zero_position[0]][zero_position[1] + 1]
board[zero_position[0]][zero_position[1] + 1] = zero
board[zero_position[0]][zero_position[1]] = tile1
return (zero_position[0], zero_position[1] + 1)
elif(type == 'u'):
tile1 = board[zero_position[0] - 1][zero_position[1]]
board[zero_position[0] - 1][zero_position[1]] = zero
board[zero_position[0]][zero_position[1]] = tile1
return (zero_position[0] - 1, zero_position[1])
elif(type == 'd'):
tile1 = board[zero_position[0] + 1][zero_position[1]]
board[zero_position[0] + 1][zero_position[1]] = zero
board[zero_position[0]][zero_position[1]] = tile1
return (zero_position[0] + 1, zero_position[1])
else:
print("\ninvalid input, plz enter again!\n")
return zero

def operate(zero_position : tuple[int], board : list, mode : int,
l : str, r : str, u : str, d : str, total_steps : int) -> tuple:
"""
test where the tile can go
the return value is an integer that represent:
0:no limits, 1:left_limit, 2:righ_limit, 3:up_limit, 4:down_limit,
5:left_up limit, 6:right_up limit, 7:left_down limit, 8:right_down limit
and do the operation for each term.
finally, return the new_zero tile
"""
print_board(board, mode)
size = len(board[0])-1 #the size of the board (start from 0)
total_steps += 1 #add one steps
ipt = 0 #input
if((zero_position[0] == 0) and (zero_position[1] != 0) and (zero_position[1] != size)): # 3
ipt = input("Enter your move (left-"+ l +", right-"+ r +", down-"+ d +") > ")
if(ipt == l):
return (movement(zero_position, board,'l'),total_steps)
elif(ipt == r):
return (movement(zero_position, board, 'r'),total_steps)
elif(ipt == d):
return (movement(zero_position, board, 'd'),total_steps)
else:
total_steps -= 1
print("\ninvalid input, plz enter again!\n")
return (zero_position, total_steps)
elif((zero_position[1] == 0) and (zero_position[0] != 0) and (zero_position[0] != size)): # 1
ipt = input("Enter your move (right-"+ r +", up-"+ r +",down-"+ d +") > ")
if(ipt == r):
return (movement(zero_position, board, 'r'),total_steps)
elif(ipt == u):
return (movement(zero_position, board, 'u'),total_steps)
elif(ipt == d):
return (movement(zero_position, board, 'd'),total_steps)
else:
total_steps -= 1
print("\ninvalid input, plz enter again!\n")
return (zero_position, total_steps)
elif((zero_position[0] == size) and (zero_position[1] !=0) and(zero_position[1] != size)): # 4
ipt = input("Enter your move (left-"+ l +", right-"+ r +", up-"+ u +") > ")
if(ipt == l):
return (movement(zero_position, board,'l'),total_steps)
elif(ipt == r):
return (movement(zero_position, board, 'r'),total_steps)
elif(ipt == u):
return (movement(zero_position, board, 'u'),total_steps)
else:
total_steps -= 1
print("\ninvalid input, plz enter again!\n")
return (zero_position, total_steps)
elif((zero_position[1] == size) and (zero_position[0] != 0) and (zero_position[0] != size)): # 2
ipt = input("Enter your move (left-"+ l +", up-"+ u +", down-"+ d +") > ")
if(ipt == l):
return (movement(zero_position, board,'l'),total_steps)
elif(ipt == u):
return (movement(zero_position, board, 'u'),total_steps)
elif(ipt == d):
return (movement(zero_position, board, 'd'),total_steps)
else:
total_steps -=1
print("\ninvalid input, plz enter again!\n")
return (zero_position, total_steps)
elif((zero_position[0] == 0) and (zero_position[1] == 0)): # 5
ipt = input("Enter your move (right-"+ r +", down-"+ d +") > ")
if(ipt == r):
return (movement(zero_position, board,'r'),total_steps)
elif(ipt == d):
return (movement(zero_position, board, 'd'),total_steps)
else:
total_steps -= 1
print("\ninvalid input, plz enter again!\n")
return (zero_position, total_steps)
elif((zero_position[0] == size) and (zero_position[1] == 0)): # 7
ipt = input("Enter your move (right-"+ r +", up-"+ r +") > ")
if(ipt == r):
return (movement(zero_position, board,'r'),total_steps)
elif(ipt == u):
return (movement(zero_position, board, 'u'),total_steps)
else:
total_steps -= 1
print("\ninvalid input, plz enter again!\n")
return (zero_position, total_steps)
elif((zero_position[0] == 0) and (zero_position[1] == size)): # 6
ipt = input("Enter your move (left-"+ l +", down-"+ d +") > ")
if(ipt == l):
return (movement(zero_position, board,'l'),total_steps)
elif(ipt == d):
return (movement(zero_position, board, 'd'),total_steps)
else:
total_steps -= 1
print("\ninvalid input, plz enter again!\n")
return (zero_position, total_steps)
elif((zero_position[0] == size) and (zero_position[1] == size)): # 8
ipt = input("Enter your move (left-"+ l +", up-"+ u +") > ")
if(ipt == l):
return (movement(zero_position, board,'l'),total_steps)
elif(ipt == u):
return (movement(zero_position, board, 'u'),total_steps)
else:
total_steps -= 1
print("\ninvalid input, plz enter again!\n")
return (zero_position, total_steps)
else: # 0
ipt = input("Enter your move (left-"+ l +", right-"+ r +", up-"+ u +", down-"+ d +") > ")
if(ipt == l):
return (movement(zero_position, board, 'l'),total_steps)
elif(ipt == r):
return (movement(zero_position, board,'r'),total_steps)
elif(ipt == u):
return (movement(zero_position, board, 'u'),total_steps)
elif(ipt == d):
return (movement(zero_position, board, 'd'),total_steps)
else:
total_steps -= 1
print("\ninvalid input, plz enter again!\n")
return (zero_position, total_steps)

def is_valid(test : list[int], zero_position : tuple[int], mod : int) -> bool:
"""
test whether the game is valid
true for valid, false for invalid
"""
ans = 0 #number of inversed pair
for i in range(len(test)):
for j in range(i+1,len(test)):
if(test[j] < test[i]):
ans+=1
if(mod == 1):
if((ans % 2) == 0):
return True
else:
return False
else: #for 4*4 mod
if((ans % 2) == 0):
if(((zero_position[0] - 3) % 2) == 0):
return True
else:
return False
else:
if(((zero_position[0] - 3) % 2) != 0):
return True
else:
return False

def eight_tile_generator() -> tuple:
"""
generate 8 tiles mode
"""
test = [0]*9 #a list aim to find out is the puzzle valid?
is_first = True
lst = []
zero_position = (2,2)
pointor = 0
board = []
while(not is_valid(test, zero_position, 1) or is_first):
test = [0]*8 #a list aim to find out is the puzzle valid?
pointor = 0
lst=[0,1,2,3,4,5,6,7,8] #the lst list out all the posible value of a tile
board = [[0,0,0],
[0,0,0],
[0,0,0]] #3*3 board from (0,0) to (2,2)
for i in range(3):
for j in range(3):
ans = limt_rand(lst)
board[i][j] = ans
if(ans != 0):
test[pointor] = ans
pointor += 1
if(ans == 0): #note the position of zero
zero_position = (i,j)
is_first = False
return (board, zero_position)


def fifteen_tile_generator() -> tuple:
"""
generate 15 tiles mode
"""
is_first = True
zero_position = (3,3)
test = [0]*16 #this is to test whether the problem is solvable
lst = []
board = []
ans = 0
while(not is_valid(test, zero_position, 2) or is_first):
lst=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15] #the lst list out all the posible value of a tile
board = [[0,0,0,0],
[0,0,0,0],
[0,0,0,0],
[0,0,0,0]] #3*3 board from (0,0) to (2,2)
test = [0]*15 #this is to test whether the problem is solvable
pointor = 0
for i in range(4):
for j in range(4):
ans = limt_rand(lst)
board[i][j] = ans
if(ans != 0):
test[pointor] = ans
pointor += 1
if(ans == 0): #note down the zero's podition
zero_position = (i,j)
is_first = False
return (board, zero_position)

def mode_1(l : str, r : str, u : str, d : str, total_steps : int) -> None:
"""
mode for 8 tiles
"""
board, zero_position = eight_tile_generator()
final = [[0,0,0],
[0,0,0],
[0,0,0]] #final answer
poiner = 1 # to keep entries in final in order
for i in range(3):
for j in range(3):
final[i][j] = poiner
poiner+=1
final[2][2] = 0 #generate final answer
while(not is_same(board,final,1)):
zero_position, total_steps = operate(zero_position, board, 1, l, r, u, d, total_steps)
print("\nstep : %d" %total_steps)
print_board(board,1)
print("\n//////////// well done!, total steps : " + str(total_steps) +" ///////////\n")


def mode_2(l : str, r : str, u : str, d : str, total_steps : int) -> None:
"""
mode for 15 tiles
"""
board, zero_position = fifteen_tile_generator()
final = [[0,0,0,0],
[0,0,0,0],
[0,0,0,0],
[0,0,0,0]] # this is the final step
poiner = 1 # to keep entries in final in order
for i in range(4): #generate final
for j in range(4):
final[i][j] = poiner
poiner+=1
final[3][3] = 0
while(not is_same(board,final,2)):
zero_position, total_steps = operate(zero_position, board, 2, l, r, u, d, total_steps)
print("\nstep : %d" %total_steps)
print_board(board,2)
print("\n//////////// well done! total steps : " + str(total_steps) + " ///////////\n")

def main() -> None:
mode_name = ''
total_steps = 0
mode_name = 0
print("""
Welcome to Kinley’s puzzle game, you will be prompted to choose playing mode,
8-number mode(mode 1) or 15-number mode(mode 2):
An 8-number puzzle has a square-framed board consisting of 8 square tiles, numbered 1 to 8,
initially placed in random order,
while a 15-number puzzle there are 15 numbered square tiles, from 1 to 15.
The game board has an empty space where one of adjacent tilesslides to.
The objective of the game is to re-arrange the tiles into a sequential order by their numbers
(left to right, top to bottom) by repeatedly making sliding moves (left, right, up or down).
The following figure shows an example of one 8-number puzzle where “INITIAL” is the starting point of the game,
and the player needs to repeatedly slide one adjacent tile, one at a time, to the unoccupied space (the empty space)
until all numbers appear sequentially, ordered from left to right, top to bottom, shown as “FINAL”.
""")
while(True):
total_steps = 0
mode_name = input("Enter “1” for 8-puzzle, “2” for 15-puzzle or “q” to end the game > ")
l,r,u,d = 'l','r','u','d'
correct_input = True
if(mode_name == 'q'):
return
elif(mode_name == '1'):
correct_input = False
while(not correct_input):
try:
l,r,u,d= input("plz enter letter represent left, right, up ,down > ")
correct_input = True
if(l == r or l ==u or l == d or r == u or r == d or u == d):
#whether input is same
correct_input = False
print("don't use same letter !")
else:
print("reminder: left-" + l + ", right-" + r + ", up-" + u + ", down-" + d)
except:
print("invalid input, plz do not use anything to divide 4 leters, eg: 'lrud'")
mode_1(l,r,u,d, total_steps)
elif(mode_name == '2'):
correct_input = False
while(not correct_input):
try:
l,r,u,d= input("plz enter letter represent left, right, up ,down > ")
correct_input = True
print("reminder: left-" + l + ", right-" + r + ", up-" + u + ", down-" + d)
except:
print("invalid input, plz do not use anything to divide 4 leters, eg: 'lrud'")
mode_2(l,r,u,d, total_steps)
else:
print(" invalid input, plz enter again!")
continue

main()

assignment 2 (得分:93/100):

这是重力四子棋,用python自带的turtle库实现(只能在python3.10及以上版本运行)"

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
import turtle as tt

scn = tt.Screen()
g_canvas = scn.getcanvas()
p = "purple" #simpler representation
b = "dark blue"
recent_click = [0,0] #restore which column the pointor click and the first space row in each column
#the position of the board
time = 0 #how many process
rate = 4
board = [[0*9],
[0] + [(x + 50,150*rate + 130) for x in range(10*rate, 170*rate, 20*rate)],
[0] + [(x + 50,130*rate + 130) for x in range(10*rate, 170*rate, 20*rate)],
[0] + [(x + 50,110*rate + 130) for x in range(10*rate, 170*rate, 20*rate)],
[0] + [(x + 50, 90*rate + 130) for x in range(10*rate, 170*rate, 20*rate)],
[0] + [(x + 50, 70*rate + 130) for x in range(10*rate, 170*rate, 20*rate)],
[0] + [(x + 50, 50*rate + 130) for x in range(10*rate, 170*rate, 20*rate)],
[0] + [(x + 50, 30*rate + 130) for x in range(10*rate, 170*rate, 20*rate)],
[0] + [(x + 50, 10*rate + 130) for x in range(10*rate, 170*rate, 20*rate)]]
#absolute position for every "coordinate"

col_pos = [0] + [(x + 50, 790) for x in range(10*rate, 170*rate, 20*rate)] #the position of the bar of each column
col = [(tt.Turtle, tt.Turtle)] #col[i][0] : the main black bar, col[i][1] : color bar
in_board = [[0]*10, #each row represent a column
[0]+['0']*9,
[0]+['0']*9,
[0]+['0']*9,
[0]+['0']*9,
[0]+['0']*9,
[0]+['0']*9,
[0]+['0']*9,
[0]+['0']*9,
[0]*10] #purple and blue in board, p for purple, b for blue

the_turtle = [[0]*9, #each row represent a column
[0]+[tt.Turtle()]*8,
[0]+[tt.Turtle()]*8,
[0]+[tt.Turtle()]*8,
[0]+[tt.Turtle()]*8,
[0]+[tt.Turtle()]*8,
[0]+[tt.Turtle()]*8,
[0]+[tt.Turtle()]*8,
[0]+[tt.Turtle()]*8] #restore circle turtle objects

path = [] #for cmp funcion to denote the path

def circle_generator(side : str, col : int, row : int) -> tt.Turtle:
""" this function generate a circle in the given column
causion: the turtle we generated always hide at first """
scn.tracer(n=5000)
t = tt.Turtle()
t.shape("circle")
t.shapesize(3)
t.color(side)
t.penup()
t.setposition(col,row)
t.ht()
scn.tracer(n=100, delay=20)
return t

def col_generator(col : int, row : int) -> tuple[tt.Turtle]:
""" this function generate the black bar in each column
causion: the turtle we generated always hide at first """
#generate color bar colar,this is why the bar can change the color
color = tt.Turtle()
color.ht()
color.shape("square")
color.color("red")
color.penup()
color.speed(0)
color.setposition(col,row)
color.shapesize(0.7,3.5)
color.setposition(col,row)

#generate main black bar
t_main = tt.Turtle()
t_main.ht()
t_main.shape("square")
t_main.penup()
t_main.speed(0)
t_main.setposition(col,row)
t_main.shapesize(0.5,3)
t_main.setposition(col,row)
return (t_main, color)

def motion_color_reveal(event : any) -> None:
""" when we move mouse to the bar area, reveal the color bar """
global board, col_pos, col, p, b, time
x = event.x
y = event.y
if(time % 2 == 0): #purple
for i in range(1,9):
col[i][1].color(p)
else: #blue
for i in range(1,9):
col[i][1].color(b)
for i in range(1,3):
if(x <= col_pos[i][0] + 28 and x >= col_pos[i][0] - 38): #col 1&2
col[i][1].st()
else:
col[i][1].ht()
for i in range(3,5):
if(x <= col_pos[i][0] + 20 and x >= col_pos[i][0] - 45): #col 3&4
col[i][1].st()
else:
col[i][1].ht()
if(x <= col_pos[5][0] + 15 and x >= col_pos[5][0] - 52): #col 5
col[5][1].st()
else:
col[5][1].ht()
if(x <= col_pos[6][0] + 11 and x >= col_pos[6][0] - 55): #col 6
col[6][1].st()
else:
col[6][1].ht()
if(x <= col_pos[7][0] + 5 and x >= col_pos[7][0] - 58): #col 7
col[7][1].st()
else:
col[7][1].ht()
if(x <= col_pos[8][0] + 4 and x >= col_pos[8][0] - 64): #col 8
col[8][1].st()
else:
col[8][1].ht()
return

def change_bar_color(side : int) -> None:
""" this function is to change the color of all 8 bar """
global col
for i in range(1,9):
col[i][1].color(side)
return

def position_turtle(x : int, y : int) -> None:
""" find where the position cilcked and give it to
"recent click" intager and set the turtle and chack if
it is the final """
global recent_click, board, col_pos, col, p, b, in_board, time, scn, the_turtle
scn.title("A2_SDS_122090249_Source") #reset the title
scn.tracer(n = 1000)
for i in range(1,9):
if(x <= col_pos[i][0] + 36.65 and x >= col_pos[i][0] - 36.65):
recent_click[0] = i
recent_click[1] = in_board[i].index('0') #find the index of the first space
if(recent_click[1] <= 8 ): #don't flow out!
if(time % 2 == 0): #purple turn
c1 = circle_generator(p, board[recent_click[1]][recent_click[0]][0],
board[recent_click[1]][recent_click[0]][1])
c1.st()
the_turtle[recent_click[0]][recent_click[1]] = c1
in_board[recent_click[0]][recent_click[1]] ='p'
change_bar_color(b)
if(is_final() != 0):
scn.listen()
scn.onclick(none_cilck, add = False)
else: #blue turn
c1 = circle_generator(b, board[recent_click[1]][recent_click[0]][0],
board[recent_click[1]][recent_click[0]][1])
c1.st()
the_turtle[recent_click[0]][recent_click[1]] = c1
in_board[recent_click[0]][recent_click[1]] ='b'
change_bar_color(p)
if(is_final() != 0):
scn.listen()
scn.onclick(none_cilck, add = False)
time += 1 #time calculator
else:
scn.title("overflow!!!")


scn.tracer(n = 100, delay = 20)
return

def is_final() -> int:
""" check whether who win
0 as no one win,
1 as purple win,
2 as blue win
3 as is full"""
global board, col_pos, col, p ,b, in_board, recent_click, time, scn, the_turtle, path
ans = 0 #the situation
bol = True
for i in range(1,len(in_board)): #is full
if(in_board[i][8] == '0'):
bol = False
if(bol):
scn.title("is full!!!")
return 3
else:
if(time % 2 == 0):#is purple win?
if(cmp('p')):
scn.title("purple win!!!")
path = []
scn.listen()
scn.onclick(none_cilck, add = False)
return 1
else:
return 0
else:
if(cmp('b')):
scn.title("blue win!!!")
path = []
scn.listen()
scn.onclick(none_cilck, add = False)
return 2
else:
return 0

def cmp(side : str, comp = [0, 0]) -> bool:
""" compare wether the four nearest circle is same """
global path, board, col_pos, col, p ,b, in_board, recent_click, time, scn, the_turtle, path
i = 1 #the pointor
while(i <= 8): #col same
comp = [recent_click[0], i]
while(in_board[comp[0]][comp[1]] == side and len(path) < 4 and i <= 8):
path.append(comp)
i += 1
comp =[recent_click[0], i]
if(in_board[comp[0]][comp[1]] != side and len(path) < 4):
path = []
break
i += 1
if(len(path) == 4):
hlt_path(path)
path = []
return True

i = 1
while(i <= 8): #row same
comp = [i, recent_click[1]]
while(in_board[comp[0]][comp[1]] == side and len(path) < 4 and i <= 8):
path.append(comp)
i += 1
comp = [i, recent_click[1]]
if(in_board[comp[0]][comp[1]] != side and len(path) < 4):
path = []
break
i += 1
if(len(path) == 4):
hlt_path(path)
path = []
return True

i = 1
while(i <= 8): #lean same 1
if(recent_click[1] + recent_click[0] - i > 0
and recent_click[1] + recent_click[0] - i <= 8):
comp = [i, recent_click[1] + recent_click[0] - i]
while(in_board[comp[0]][comp[1]] == side and len(path) < 4):
path.append(comp)
i += 1
comp = [i, recent_click[1] + recent_click[0] - i]
if(in_board[comp[0]][comp[1]] != side and len(path) < 4):
path = []
break
i += 1
if(len(path) == 4):
hlt_path(path)
path = []
return True

i = 1
while(i <= 8): #lean same 2
if(recent_click[1] + recent_click[0] - i > 0
and recent_click[1] + recent_click[0] - i <= 8):
comp = [recent_click[1] + recent_click[0] - i, i]
while(in_board[comp[0]][comp[1]] == side and len(path) < 4):
path.append(comp)
i += 1
comp = [recent_click[1] + recent_click[0] - i,i]
if(in_board[comp[0]][comp[1]] != side and len(path) < 4):
path = []
break
i += 1
if(len(path) == 4):
hlt_path(path)
path = []
return True

i = 1
while(i <= 8): #lean same 3
if(recent_click[1] - recent_click[0] + i > 0
and ((recent_click[1] - recent_click[0] + i) <= 8)):
comp = [recent_click[1] - recent_click[0] + i, i]
while(in_board[comp[0]][comp[1]] == side and len(path) < 4):
path.append(comp)
i += 1
comp = [recent_click[1] - recent_click[0] + i, i]
if(in_board[comp[0]][comp[1]] != side and len(path) < 4):
path = []
break
i += 1
if(len(path) == 4):
hlt_path(path)
path = []
return True

i = 1
while(i <= 8): #lean same 4
if((recent_click[1] - recent_click[0] + i) > 0
and (recent_click[1] - recent_click[0] + i) <= 8):
comp = [i, recent_click[1] - recent_click[0] + i]
while(in_board[comp[0]][comp[1]] == side and len(path) < 4):
path.append(comp)
i += 1
comp = [i, recent_click[1] - recent_click[0] + i]
if(in_board[comp[0]][comp[1]] != side and len(path) < 4):
path = []
break
i += 1
if(len(path) == 4):
hlt_path(path)
path = []
return True
return False

def hlt_path(path : list[list]) -> None:
""" highlight the wining point """
global the_turtle, scn, board
scn.tracer(n = 7000)
for i in range(len(path)):
the_turtle[path[i][0]][path[i][1]].pencolor("red")
the_turtle[path[i][0]][path[i][1]].shapesize(outline = 6)
scn.tracer(n = 100, delay =10)
return

def none_cilck(x : int, y : int) -> None:
"""invalid the mouse operation"""
return

def main() -> None:
global board, col_pos, col, p ,b, in_board, recent_click, time, scn, the_turtle, g_canvas
#define the screen
scn.setworldcoordinates(13,800,800,13)
scn.title("A2_SDS_122090249_Source")
scn.tracer(n = 100, delay = 20)

#define the column
for i in range(1,9):
col.append(col_generator(col_pos[i][0], col_pos[i][1]))
for i in range(1,9):
col[i][0].st()

#motion of mouse on screen
g_canvas.bind("<Motion>", motion_color_reveal)
scn.listen()
scn.onclick(position_turtle)
scn.mainloop()

main()

assignment 3 (得分:100/100):

这是贪吃蛇,,但是不以碰撞自身判定结束而是以被monster抓获判定,这个小游戏使用了python自带的turtle库(只能在python3.10及以上版本运行)"

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
import turtle as tt
from functools import partial
import random
from math import atan, pi

g_scn = 0 #the screan object
g_intro = 0 #the introduction bar
g_status = 0 #the playing area
g_keypressed = "Paused" #the recent key press
g_time = 0 #globaltime
g_snake = 0 #the snake
g_snake_pos = [(0,0)] #the position of snake
g_snake_sz = 5 #the len of the snake
g_contact = 0 #total score player get
g_food_pos = [] #the position of food
g_food_value = [0,1,2,3,4] #the value of each food
g_food_condition = [True]*5 #a list of bool object reprecent hide and unhide
g_food_object = [] #the food object
g_eated = [] #the food that is eaten
g_is_space = False #how many time space, odd: True, even: False
g_last_dir = 0 #how the last input befor space
g_is_eating = True #test if snake are eating
g_is_end = False #recorde if it is end
g_is_win = False #is win but not end

#default setings
COLOR_BODY = ("blue", "black")
COLOR_HEAD = "red"
COLOR_MONSTER = "purple"
FONT = ("Arial",16,"normal")

KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, KEY_SPACE = \
"Up", "Down", "Left", "Right", "space"

HEADING_BY_KEY = {KEY_UP:90, KEY_DOWN:270, KEY_LEFT:180, KEY_RIGHT:0}

#the "tiles position"
#20*20 tile and the center point is on the coordinate
g_x = [x for x in range(-240, 260, 20)]
g_y = [y for y in range(-280, 220, 20)]

def Screan_setup() -> tt.TurtleScreen:
""" set up the screan """

scn = tt.Screen()
scn.tracer(0) # disable auto screen refresh, 0=disable, 1=enable
scn.title("Snake by Lei Mingcong")
scn.setup(500+120, 500+120+80)
scn.mode("standard")
return scn

def create_turtle(x : int, y : int, color="red", border="black") -> tt.Turtle:
""" create a turtle with default position, colar and outline """

t = tt.Turtle("square")
t.color(border, color)
t.up()
t.goto(x,y)
return t

def configure_play_area() -> tuple[tt.Turtle]:
""" create the player area and introduction bar """

# motion border
m = create_turtle(0,0,"","black")
m.shapesize(25,25,5)
m.goto(0,-40) # shift down half the status

# status border
s = create_turtle(0,0,"","black")
s.shapesize(4,25,5)
s.goto(0,250) # shift up half the motion

# introduction
intro = create_turtle(-200,150)
intro.hideturtle()
intro.write("Click anywhere to start the game .....", font=("Arial",16,"normal"))

# statuses
status = create_turtle(0,0,"","black")
status.hideturtle()
status.goto(-200,s.ycor()) #y coordinate are same

return intro, status

def update_status() -> None:
""" update the movement """
global g_scn, g_intro, g_status, g_keypressed, g_time, g_contact

g_status.clear()
g_status.write(f"Contact: {g_contact} Time: {g_time} Motion: {g_keypressed}", font=('arial',15,'bold'))
g_scn.update()
return

def on_arrowkey_pressed(key : str) -> None:
""" when key pressed, update the introduction bar """
global g_keypressed, g_is_space, g_last_dir, g_is_end

if(g_is_end):
return

if(key == "space"):
g_is_space = not g_is_space
if(g_is_space):
g_keypressed = "Paused"
else:
g_keypressed = g_last_dir
elif(not g_is_space): #if not pause
g_keypressed = key
g_last_dir = g_keypressed
set_snake_heading(key)
else: #if pause
g_is_space = False #countinue
g_keypressed = key
g_last_dir = g_keypressed
set_snake_heading(key)
update_status()
return

def set_snake_heading(key : str) -> None:
""" set the heading direction of snake """
global g_is_end

if(g_is_end):
return #invalid the turtle generator that the snake head will stop updating after being catch

if(key in HEADING_BY_KEY.keys()):
g_snake.setheading( HEADING_BY_KEY[key] ) #set the positio that snake is heading
return

def is_valid() -> bool:
""" this funtion is to determine whether the movement is valid """
global g_snake, g_keypressed, g_snake_pos

x, y = g_snake.pos()
x, y = round(x), round(y)
valid = True
#is snake out of range?
if((y >= 197 and g_keypressed == "Up") or (y <= -277 and g_keypressed == "Down") or
(x >= 237 and g_keypressed == "Right") or (x <= -237 and g_keypressed == "Left")):
valid = False

#is snake crash itself?
if(((x,y+20) in g_snake_pos and g_keypressed == "Up") or ((x,y-20) in g_snake_pos and g_keypressed == "Down") or
((x+20,y) in g_snake_pos and g_keypressed == "Right") or ((x-20,y) in g_snake_pos and g_keypressed == "Left")):
valid = False

return valid

def on_timer_snake() -> None:
""" main movement of the snake """
global g_keypressed, g_snake, g_snake_pos, g_food_pos, g_snake_sz
global g_is_end, g_contact, g_is_eating, g_is_win, g_eated

if(g_is_end): #if end, stop every thing
return

over = 0 #the turtle that print "Game over"
x_s, y_s = g_snake.pos() #position of snake head
x_s, y_s = round(x_s), round(y_s) #use "smaller than four ignore, bigger than five added method to change it into integer"
x_m, y_m = g_monster.pos() #position of monster
x_m, y_m = round(x_m), round(y_m)
if(abs(x_s - x_m) < 20 and abs(y_s-y_m) < 20): #test is end
g_is_end = True
over = create_turtle(x_m+10, y_m+10,"purple", "black")
over. pencolor("purple")
over.ht()
over.write("Game over!!", font=('arial',15,'bold'))
over.goto(x_m, y_m)
over.st()
g_scn.update()
return

if(g_keypressed == "Paused"): #if paused, stop counting but still can be catch
if(not g_is_eating):
g_scn.ontimer(on_timer_snake, 200)
else:
g_scn.ontimer(on_timer_snake,300) #do not try to use pause to eat faster, see row 227 !!!!!
return

if(is_valid()): #is the movement valid?
# Clone the head as body
g_snake.color(*COLOR_BODY)
g_snake.stamp()
g_snake.color(COLOR_HEAD)

# Advance snake
g_snake.forward(20)
x, y = g_snake.pos()
x = round(x)
y = round(y)
g_snake_pos = [(x,y)] + g_snake_pos.copy()
if(len(g_snake.stampItems) > g_snake_sz):
# Shifting or extending the tail.
# Remove the last square on Shifting.
g_snake.clearstamps(1)
g_snake_pos.pop()
if(g_is_eating and (len(g_snake.stampItems) == g_snake_sz)): #is eating and fully extend
g_is_eating = False #end the eating motion

if(g_is_win and (len(g_snake.stampItems) == g_snake_sz)): #is win and fully extend
over = create_turtle(x_s+5, y_s+5,"", "red")
over.pencolor("red")
over.ht()
over.write("Winner!!", font=('arial',15,'bold'))
g_scn.update()
g_is_end = True #end the game
return

i = 0
while(i < len(g_food_pos)): #if eat the food
if(not((i) in g_eated) and g_food_condition[i] and (x,y) == g_food_pos[i]):
#i is not eated, not hided
g_is_eating = True #is eating the food
g_snake_sz += g_food_value[i] + 1
g_food_object[i].clear()
g_eated.append(i)
i += 1

if(len(g_eated) == 5): #winer
g_is_win = True #win but not end, need to extend

g_scn.update()
if(not g_is_eating):
g_scn.ontimer(on_timer_snake, 200)
else: #if eating, slow down
g_scn.ontimer(on_timer_snake, 300) #see row 178
return

def on_timer_monster() -> None:
""" main movement of monster """
global g_monster, g_scn, g_keypressed, g_snake, g_is_end, g_contact
if(g_is_end):
return

rand_time = rand(250,350) #take a random rate,the monster will not that faster than snake to reduce the difficalty
x_s, y_s = g_snake.pos() #positon of snake
x_s, y_s = round(x_s), round(y_s)
x_m, y_m = g_monster.pos() #position of monster
x_m, y_m = round(x_m), round(y_m)

if(x_s - x_m > 0): #atan is from -pi/2 to pi/2
dir = (360/(2*pi)) * atan((y_s - y_m) / (x_s - x_m + 0.0000001)) #the dir that moster need to go to (catch)
else: #atan is from -pi/2 to pi/2
dir = 180 + (360/(2*pi)) * atan((y_s - y_m) / (x_s - x_m - 0.0000001)) #the dir that moster need to go to (catch)

if( dir > 45 and dir <= 135): #change direction in 4 key direction
dir = 90
elif( dir > 135 and dir <= 225):
dir = 180
elif( dir > 225 and dir <= 315):
dir = 270
else:
dir = 0

g_monster.setheading(dir)
g_monster.forward(20)

for i in range(len(g_snake_pos)): #count the contact time
if(abs(x_m - g_snake_pos[i][0]) < 20 and abs(y_m - g_snake_pos[i][1]) < 20): #contact with monster
g_contact += 1
update_status() #update quiker so that if win the contact is valid

g_scn.update()
g_scn.ontimer(on_timer_monster, rand_time)
return

def timer() -> None:
""" counting the gaming time """
global g_time, g_scn, g_keypressed
if(g_is_end):
return

g_time += 1
update_status()
g_scn.ontimer(timer,1000)
return

def on_timer_food() -> None:
""" main function for food """
global g_eated, g_scn, g_food_pos, g_keypressed, g_food_object, g_x, g_y, g_time, g_is_end

rand_time = 1000*rand(5,10)
rand_tile = rand(0,4,g_eated) #take randomly number from eated
if(g_is_end):
return

if(g_food_condition[rand_tile]):
g_food_object[rand_tile].clear() #hide it
else:
g_food_object[rand_tile].write(g_food_value[rand_tile] + 1, font=("Arial",10,"normal")) #choose one to hide of unhide
g_food_condition[rand_tile] = not g_food_condition[rand_tile] #reverse the condition

g_scn.ontimer(on_timer_food, rand_time)
return



def None_motion(x : int, y : int) -> None:
""" a spaced funcion """
return

def start_game(x : int, y : int) -> None:
""" main fumction """
global g_scn, g_intro, g_status, g_keypressed, g_is_end, g_food_value

g_scn.onscreenclick(None_motion)
g_intro.clear()
if(g_is_end):
return

#bind the keys
g_scn.onkey(partial(on_arrowkey_pressed,KEY_UP), KEY_UP)
g_scn.onkey(partial(on_arrowkey_pressed,KEY_DOWN), KEY_DOWN)
g_scn.onkey(partial(on_arrowkey_pressed,KEY_LEFT), KEY_LEFT)
g_scn.onkey(partial(on_arrowkey_pressed,KEY_RIGHT), KEY_RIGHT)
g_scn.onkey(partial(on_arrowkey_pressed,KEY_SPACE), KEY_SPACE)

#bind the timers
g_scn.ontimer(on_timer_food, 1000*rand(5,10))
g_scn.ontimer(timer, 1000)
g_scn.ontimer(on_timer_snake, 100)
g_scn.ontimer(on_timer_monster, 300)

for i in range(len(g_food_value)): #get new food objects
x_rand = rand(0,24,x_is_used)
y_rand = rand(0,24,y_is_used)
x = g_x[x_rand]
y = g_y[y_rand]
x_is_used.append(x_rand)
y_is_used.append(y_rand)
food = create_turtle(x-1,y-6,"","black")
food.ht()
food.write(g_food_value[i] + 1, font=("Arial",10,"normal"))
g_food_object.append(food)
g_food_pos.append((x,y))
return

def rand(x : int , y : int, ban = []) -> int:
""" this is for generate random int from x to y, without some int """
l = list(range(x, y+1))
if(len(ban) >= len(l) or ban == []):
return random.randint(x,y)
else:
for i in range(len(ban)):
if(ban[i] in l):
l.remove(ban[i])
return l[random.randint(0,len(l)-1)] #randomly take a number from l

if(__name__ == "__main__"):
""" This is the main function """

x_is_used = [] #avoid being same position in food generation process
y_is_used = []
g_scn = Screan_setup()
g_intro, g_status = configure_play_area()
update_status()
g_monster = create_turtle(g_x[rand(1, 23, [11,12,13])]-10, g_y[rand(1, 23, [13,14,15])]+10,"purple", "black")
g_snake = create_turtle(0,0,"red", "black")

g_scn.onscreenclick(start_game) #main function

g_scn.update() #after all the change done, update the screen
g_scn.listen()
g_scn.mainloop()

最后庆祝CSC1002 2023 Spring 顺利完结!