owned this note
owned this note
Published
Linked with GitHub
# 資研社課
## 俄羅斯方塊 簡介
俄羅斯方塊是一款經典的益智遊戲,由俄羅斯的遊戲設計師Alexey Pajitnov於1984年創作。這個遊戲的獨特之處在於玩家需要控制下落的七種不同形狀的方塊,以創造完整的水平行,當水平行被填滿時,該行將消失,玩家獲得分數。目標是在方塊堆積過高之前,清除盡可能多的行。
以下是俄羅斯方塊的一些基本元素和規則:
**方塊種類**: 俄羅斯方塊包括七種不同形狀的方塊,它們由四個小正方形組成,可以是直的、橫的、L形、J形、S形、Z形或正方形。
**控制**: 玩家可以使用鍵盤上的方向鍵左、右、下,來控制方塊的移動和旋轉,使其在屏幕上移動和變形。
**下落速度**: 遊戲開始時,方塊下降速度較慢,但隨著時間的推移,速度將逐漸增加,增加了遊戲的難度。
**消行**: 當一行方塊填滿整個水平行時,該行將被清除,玩家得分。清除多行同時可以獲得更高的分數。
**遊戲結束**: 如果方塊堆疊到達遊戲區的頂部,則遊戲結束。
**得分**: 玩家的得分通常是根據清除的行數和遊戲的難度而定。
俄羅斯方塊以其簡單但極富挑戰性的遊戲方式,成為了電子遊戲歷史上最受歡迎和廣泛認識的遊戲之一,並在各種不同的遊戲平台上持續存在。
## 設定
* 安裝**pygame**
```
pip install pygame
```
* 引入函式庫
```python!
import random #隨機生成數
import sys #結束遊戲
import time #計時
import pygame #遊戲背景
SCREEN_WIDTH, SCREEN_HEIGHT = 450, 750
BG_COLOR = (40, 40, 60) # 背景色
BLOCK_COL_NUM = 10 # 每行的方格數
SIZE = 30 # 每個小方格大小
BLOCK_ROW_NUM = 25 # 每列的方個數
BORDER_WIDTH = 4 # 遊戲區邊框寬度
RED = (200, 30, 30) # 紅色,GAME OVER 的字型顏色
```
## 方塊圖形設定
```python!
# S形方塊
block_s = [['.OO',
'OO.',
'...'],
['O..',
'OO.',
'.O.']]
# Z形方塊
block_z = [['OO.',
'.OO',
'...'],
['.O.',
'OO.',
'O..']]
# I型方塊
block_i = [['.O..',
'.O..',
'.O..',
'.O..'],
['....',
'....',
'OOOO',
'....']]
# O型方塊
block_o = [['OO',
'OO']]
# J型方塊
block_j = [['O..',
'OOO',
'...'],
['.OO',
'.O.',
'.O.'],
['...',
'OOO',
'..O'],
['.O.',
'.O.',
'OO.']]
# L型方塊
block_l = [['..O',
'OOO',
'...'],
['.O.',
'.O.',
'.OO'],
['...',
'OOO',
'O..'],
['OO.',
'.O.',
'.O.']]
# T型方塊
block_t = [['.O.',
'OOO',
'...'],
['.O.',
'.OO',
'.O.'],
['...',
'OOO',
'.O.'],
['.O.',
'OO.',
'.O.']]
```
## 背景設定
```python!
pygame.init()
screen = pygame.display.set_mode((SCREEN_WIDTH,SCREEN_HEIGHT))
pygame.display.set_caption('俄羅斯方塊')
current_block = get_block() # 當前圖形
current_block_start_row = -2 # 當前圖片從哪一行開始顯示圖形
current_block_start_col = 4 # 當前圖形從哪一列開始顯示
next_block = get_block() # 下一個圖形
last_time = time.time()
speed = 0.5 # 降落的速度
speed_info = '1' # 顯示的速度等級
stop_all_block_list = [['.' for i in range(BLOCK_COL_NUM)] for j in range(BLOCK_ROW_NUM)]# 定義一個列表,用來儲存所有的已經停止移動的形狀
# 字型
font = pygame.font.Font('yh.ttf', 24) # 黑體24
game_over_font = pygame.font.Font("yh.ttf", 72)
game_over_font_width, game_over_font_height = game_over_font.size('GAME OVER')
game_again_font_width, game_again_font_height = font.size('滑鼠點選任意位置,再來一局')
score = 0 #得分
game_over = False #遊戲是否結束
#建立計時器
```
## 判斷遊戲是否結束
```python!
def judge_game_over(stop_all_block_list):
if "O" in stop_all_block_list[0]:
return True
```
## 設置速度
```python!
def change_speed(score):
speed_level = [("1", 0.5, 0, 20), ("2", 0.4, 21, 50), ("3", 0.3, 51, 100), ("4", 0.2, 101, 200), ("5", 0.1, 201, None)]
for speed_info, speed, score_start, score_stop in speed_level:
if score_stop and score_start <= score <= score_stop:
return speed_info, speed
elif score_stop is None and score >= score_start:
return speed_info, speed
```
## 消除方格
```python!
def judge_lines(stop_all_block_list):
move_row_list = list()
for row, line in enumerate(stop_all_block_list):
if "." not in line:
stop_all_block_list[row] = ['.' for _ in range(len(line))]
move_row_list.append(row)
if not move_row_list:
return 0
for row in move_row_list:
stop_all_block_list.pop(row)
stop_all_block_list.insert(0, ['.' for _ in range(len(line))])
return len(move_row_list) * 10
```
## 改變列表
```python!
def add_to_stop_all_block_list(stop_all_block_list, current_block, current_block_start_row, current_block_start_col):
for row, line in enumerate(current_block):
for col, block in enumerate(line):
if block != '.':
stop_all_block_list[current_block_start_row + row][current_block_start_col + col] = "O"
```
## 改變遊戲介面
```python!
def change_current_block_style(current_block):
current_block_style_list = None
for block_style_list in [block_s, block_i, block_j, block_l, block_o, block_t, block_z]:
if current_block in block_style_list:
current_block_style_list = block_style_list
index = current_block_style_list.index(current_block)
index += 1
index = index % len(current_block_style_list)
return current_block_style_list[index]
```
## 判斷移動是否合法
* 右
```python!
def judge_move_right(current_block, current_block_start_col):
for col in range(len(current_block[0]) - 1, -1, -1):
col_list = [line[col] for line in current_block]
if 'O' in col_list and current_block_start_col + col >= BLOCK_COL_NUM:
return False
return True
```
* 左
```python!
def judge_move_left(current_block, current_block_start_col):
for col in range(len(current_block[0])):
col_list = [line[col] for line in current_block]
if 'O' in col_list and current_block_start_col + col < 0:
return False
return True
```
## 判斷是否撞到底部或其他圖型
```python!
def judge_move_down(current_block, current_block_start_row, current_block_start_col, stop_all_block_list):
stop_all_block_position = list()
for row, line in enumerate(stop_all_block_list):
for col, block in enumerate(line):
if block != ".":
stop_all_block_position.append((row, col))
for row, line in enumerate(current_block):
if 'O' in line and current_block_start_row + row >= BLOCK_ROW_NUM:
return False
for col, block in enumerate(line):
if block != "." and (current_block_start_row + row, current_block_start_col + col) in stop_all_block_position:
return False
return True
```
## 建圖
```python!
def get_block():
"""
建立一個圖形
"""
block_style_list = random.choice([block_s, block_i, block_j, block_l, block_o, block_t, block_z])
return random.choice(block_style_list)
```
## 建立計時器
```python!
clock = pygame.time.Clock()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
if judge_move_left(current_block, current_block_start_col - 1):
current_block_start_col -= 1
elif event.key == pygame.K_RIGHT:
if judge_move_right(current_block, current_block_start_col + 1):
current_block_start_col += 1
elif event.key == pygame.K_UP:
current_block_next_style = change_current_block_style(current_block)
if judge_move_left(current_block_next_style, current_block_start_col) and
judge_move_right(current_block_next_style, current_block_start_col) and
judge_move_down(current_block, current_block_start_row, current_block_start_col, stop_all_block_list):
# 判斷新的樣式沒有越界
current_block = current_block_next_style
elif event.key == pygame.K_DOWN:
# 判斷是否可以向下移動,如果碰到底部或者其它的圖形就不能移動了
if judge_move_down(current_block, current_block_start_row + 1, current_block_start_col, stop_all_block_list):
current_block_start_row += 1
elif event.type == pygame.MOUSEBUTTONDOWN and event.button:
if game_over:
# 重置遊戲用到的變數
current_block = get_block() # 當前圖形
current_block_start_row = -2 # 當前圖片從哪一行開始顯示圖形
current_block_start_col = 4 # 當前圖形從哪一列開始顯示
next_block = get_block() # 下一個圖形
stop_all_block_list = [['.' for i in range(BLOCK_COL_NUM)] for j in range(BLOCK_ROW_NUM)]
score = 0
game_over = False
# 判斷是否修改當前圖形顯示的起始行
if not game_over and time.time() - last_time > speed:
last_time = time.time()
# 判斷是否可以向下移動,如果碰到底部或者其它的圖形就不能移動了
if judge_move_down(current_block, current_block_start_row + 1, current_block_start_col, stop_all_block_list):
current_block_start_row += 1
else:
# 將這個圖形儲存到統一的列表中,這樣便於判斷是否成為一行
add_to_stop_all_block_list(stop_all_block_list, current_block, current_block_start_row, current_block_start_col)
# 判斷是否有同一行的,如果有就消除,且加上分數
score += judge_lines(stop_all_block_list)
# 判斷遊戲是否結束(如果第一行中間有O那麼就表示遊戲結束)
game_over = judge_game_over(stop_all_block_list)
# 調整速度
speed_info, speed = change_speed(score)
# 建立新的圖形
current_block = next_block
next_block = get_block()
# 重置資料
current_block_start_col = 4
current_block_start_row = -2
# 畫背景(填充背景色)
screen.fill(BG_COLOR)
# 畫遊戲區域分隔線
pygame.draw.line(screen, (100, 40, 200), (SIZE * BLOCK_COL_NUM, 0), (SIZE * BLOCK_COL_NUM, SCREEN_HEIGHT), BORDER_WIDTH)
# 顯示當前圖形
for row, line in enumerate(current_block):
for col, block in enumerate(line):
if block != '.':
pygame.draw.rect(screen, (20, 128, 200), ((current_block_start_col + col) * SIZE, (current_block_start_row + row) * SIZE, SIZE, SIZE), 0)
# 顯示所有停止移動的圖形
for row, line in enumerate(stop_all_block_list):
for col, block in enumerate(line):
if block != '.':
pygame.draw.rect(screen, (20, 128, 200), (col * SIZE, row * SIZE, SIZE, SIZE), 0)
# 畫網格線 豎線
for x in range(BLOCK_COL_NUM):
pygame.draw.line(screen, (0, 0, 0), (x * SIZE, 0), (x * SIZE, SCREEN_HEIGHT), 1)
# 畫網格線 橫線
for y in range(BLOCK_ROW_NUM):
pygame.draw.line(screen, (0, 0, 0), (0, y * SIZE), (BLOCK_COL_NUM * SIZE, y * SIZE), 1)
# 顯示右側(得分、速度、下一行圖形)
# 得分
score_show_msg = font.render('得分: ', True, (255, 255, 255))
screen.blit(score_show_msg, (BLOCK_COL_NUM * SIZE + 10, 10))
score_show_msg = font.render(str(score), True, (255, 255, 255))
screen.blit(score_show_msg, (BLOCK_COL_NUM * SIZE + 10, 50))
# 速度
speed_show_msg = font.render('速度: ', True, (255, 255, 255))
screen.blit(speed_show_msg, (BLOCK_COL_NUM * SIZE + 10, 100))
speed_show_msg = font.render(speed_info, True, (255, 255, 255))
screen.blit(speed_show_msg, (BLOCK_COL_NUM * SIZE + 10, 150))
# 下一個圖形(文字提示)
next_style_msg = font.render('下一個: ', True, (255, 255, 255))
screen.blit(next_style_msg, (BLOCK_COL_NUM * SIZE + 10, 200))
# 下一個圖形(圖形)
for row, line in enumerate(next_block):
for col, block in enumerate(line):
if block != '.':
pygame.draw.rect(screen, (20, 128, 200), (320 + SIZE * col, (BLOCK_COL_NUM + row) * SIZE, SIZE, SIZE), 0)
# 顯示這個方格的4個邊的顏色
# 左
pygame.draw.line(screen, (0, 0, 0), (320 + SIZE * col, (BLOCK_COL_NUM + row) * SIZE), (320 + SIZE * col, (BLOCK_COL_NUM + row + 1) * SIZE), 1)
# 上
pygame.draw.line(screen, (0, 0, 0), (320 + SIZE * col, (BLOCK_COL_NUM + row) * SIZE), (320 + SIZE * (col + 1), (BLOCK_COL_NUM + row) * SIZE), 1)
# 下
pygame.draw.line(screen, (0, 0, 0), (320 + SIZE * col, (BLOCK_COL_NUM + row + 1) * SIZE), (320 + SIZE * (col + 1), (BLOCK_COL_NUM + row + 1) * SIZE), 1)
# 右
pygame.draw.line(screen, (0, 0, 0), (320 + SIZE * (col + 1), (BLOCK_COL_NUM + row) * SIZE), (320 + SIZE * (col + 1), (BLOCK_COL_NUM + row + 1) * SIZE), 1)
# 顯示遊戲結束畫面
if game_over:
game_over_tips = game_over_font.render('GAME OVER', True, RED)
screen.blit(game_over_tips, ((SCREEN_WIDTH - game_over_font_width) // 2, (SCREEN_HEIGHT - game_over_font_height) // 2))
# 顯示"滑鼠點選任意位置,再來一局"
game_again = font.render('滑鼠點選任意位置,再來一局', True, RED)
screen.blit(game_again, ((SCREEN_WIDTH - game_again_font_width) // 2, (SCREEN_HEIGHT - game_again_font_height) // 2 + 80))
# 重新整理顯示(此時視窗才會真正的顯示)
pygame.display.update()
# FPS(每秒鐘顯示畫面的次數)
clock.tick(60) # 通過一定的延時,實現1秒鐘能夠迴圈60次
```
## 完整程式碼
```python!
import random
import sys
import time
import pygame
block_s = [['.OO',
'OO.',
'...'],
['O..',
'OO.',
'.O.']]
block_z = [['OO.',
'.OO',
'...'],
['.O.',
'OO.',
'O..']]
block_i = [['.O..',
'.O..',
'.O..',
'.O..'],
['....',
'....',
'OOOO',
'....']]
block_o = [['OO',
'OO']]
block_j = [['O..',
'OOO',
'...'],
['.OO',
'.O.',
'.O.'],
['...',
'OOO',
'..O'],
['.O.',
'.O.',
'OO.']]
block_l = [['..O',
'OOO',
'...'],
['.O.',
'.O.',
'.OO'],
['...',
'OOO',
'O..'],
['OO.',
'.O.',
'.O.']]
block_t = [['.O.',
'OOO',
'...'],
['.O.',
'.OO',
'.O.'],
['...',
'OOO',
'.O.'],
['.O.',
'OO.',
'.O.']]
SCREEN_WIDTH, SCREEN_HEIGHT = 450, 750
BG_COLOR = (40, 40, 60)
BLOCK_COL_NUM = 10
SIZE = 30
BLOCK_ROW_NUM = 25
BORDER_WIDTH = 4
RED = (200, 30, 30)
def judge_game_over(stop_all_block_list):
if "O" in stop_all_block_list[0]:
return True
def change_speed(score):
speed_level = [("1", 0.5, 0, 20), ("2", 0.4, 21, 50), ("3", 0.3, 51, 100), ("4", 0.2, 101, 200), ("5", 0.1, 201, None)]
for speed_info, speed, score_start, score_stop in speed_level:
if score_stop and score_start <= score <= score_stop:
return speed_info, speed
elif score_stop is None and score >= score_start:
return speed_info, speed
def judge_lines(stop_all_block_list):
move_row_list = list()
for row, line in enumerate(stop_all_block_list):
if "." not in line:
stop_all_block_list[row] = ['.' for _ in range(len(line))]
move_row_list.append(row)
if not move_row_list:
return 0
for row in move_row_list:
stop_all_block_list.pop(row)
stop_all_block_list.insert(0, ['.' for _ in range(len(line))])
return len(move_row_list) * 10
def add_to_stop_all_block_list(stop_all_block_list, current_block, current_block_start_row, current_block_start_col):
for row, line in enumerate(current_block):
for col, block in enumerate(line):
if block != '.':
stop_all_block_list[current_block_start_row + row][current_block_start_col + col] = "O"
def change_current_block_style(current_block):
current_block_style_list = None
for block_style_list in [block_s, block_i, block_j, block_l, block_o, block_t, block_z]:
if current_block in block_style_list:
current_block_style_list = block_style_list
index = current_block_style_list.index(current_block)
index += 1
index = index % len(current_block_style_list)
return current_block_style_list[index]
def judge_move_right(current_block, current_block_start_col):
for col in range(len(current_block[0]) - 1, -1, -1):
col_list = [line[col] for line in current_block]
if 'O' in col_list and current_block_start_col + col >= BLOCK_COL_NUM:
return False
return True
def judge_move_left(current_block, current_block_start_col):
for col in range(len(current_block[0])):
col_list = [line[col] for line in current_block]
if 'O' in col_list and current_block_start_col + col < 0:
return False
return True
def judge_move_down(current_block, current_block_start_row, current_block_start_col, stop_all_block_list):
stop_all_block_position = list()
for row, line in enumerate(stop_all_block_list):
for col, block in enumerate(line):
if block != ".":
stop_all_block_position.append((row, col))
for row, line in enumerate(current_block):
if 'O' in line and current_block_start_row + row >= BLOCK_ROW_NUM:
return False
for col, block in enumerate(line):
if block != "." and (current_block_start_row + row, current_block_start_col + col) in stop_all_block_position:
return False
return True
def get_block():
block_style_list = random.choice([block_s, block_i, block_j, block_l, block_o, block_t, block_z])
return random.choice(block_style_list)
def main():
pygame.init()
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption('俄羅斯方塊')
current_block = get_block()
current_block_start_row = -2
current_block_start_col = 4
next_block = get_block()
last_time = time.time()
speed = 0.5
speed_info = '1'
stop_all_block_list = [['.' for i in range(BLOCK_COL_NUM)] for j in range(BLOCK_ROW_NUM)]
font = pygame.font.Font(r"C:\Users\USER\Music\Desktop\play\kaiu.ttf", 24)
game_over_font = pygame.font.Font(r"C:\Users\USER\Music\Desktop\play\kaiu.ttf" , 72)
game_over_font_width, game_over_font_height = game_over_font.size('GAME OVER')
game_again_font_width, game_again_font_height = font.size('滑鼠點選任意位置,再來一局')
score = 0
game_over = False
clock = pygame.time.Clock()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
if judge_move_left(current_block, current_block_start_col - 1):
current_block_start_col -= 1
elif event.key == pygame.K_RIGHT:
if judge_move_right(current_block, current_block_start_col + 1):
current_block_start_col += 1
elif event.key == pygame.K_UP:
current_block_next_style = change_current_block_style(current_block)
if judge_move_left(current_block_next_style, current_block_start_col) and judge_move_right(current_block_next_style, current_block_start_col) and judge_move_down(current_block, current_block_start_row, current_block_start_col, stop_all_block_list):
current_block = current_block_next_style
elif event.key == pygame.K_DOWN:
if judge_move_down(current_block, current_block_start_row + 1, current_block_start_col, stop_all_block_list):
current_block_start_row += 1
elif event.type == pygame.MOUSEBUTTONDOWN and event.button:
if game_over:
current_block = get_block()
current_block_start_row = -2
current_block_start_col = 4
next_block = get_block()
stop_all_block_list = [['.' for i in range(BLOCK_COL_NUM)] for j in range(BLOCK_ROW_NUM)]
score = 0
game_over = False
if not game_over and time.time() - last_time > speed:
last_time = time.time()
if judge_move_down(current_block, current_block_start_row + 1, current_block_start_col, stop_all_block_list):
current_block_start_row += 1
else:
add_to_stop_all_block_list(stop_all_block_list, current_block, current_block_start_row, current_block_start_col)
score += judge_lines(stop_all_block_list)
game_over = judge_game_over(stop_all_block_list)
speed_info, speed = change_speed(score)
current_block = next_block
next_block = get_block()
current_block_start_col = 4
current_block_start_row = -2
screen.fill(BG_COLOR)
pygame.draw.line(screen, (100, 40, 200), (SIZE * BLOCK_COL_NUM, 0), (SIZE * BLOCK_COL_NUM, SCREEN_HEIGHT), BORDER_WIDTH)
for row, line in enumerate(current_block):
for col, block in enumerate(line):
if block != '.':
pygame.draw.rect(screen, (20, 128, 200), ((current_block_start_col + col) * SIZE, (current_block_start_row + row) * SIZE, SIZE, SIZE), 0)
for row, line in enumerate(stop_all_block_list):
for col, block in enumerate(line):
if block != '.':
pygame.draw.rect(screen, (20, 128, 200), (col * SIZE, row * SIZE, SIZE, SIZE), 0)
for x in range(BLOCK_COL_NUM):
pygame.draw.line(screen, (0, 0, 0), (x * SIZE, 0), (x * SIZE, SCREEN_HEIGHT), 1)
for y in range(BLOCK_ROW_NUM):
pygame.draw.line(screen, (0, 0, 0), (0, y * SIZE), (BLOCK_COL_NUM * SIZE, y * SIZE), 1)
score_show_msg = font.render('得分: ', True, (255, 255, 255))
screen.blit(score_show_msg, (BLOCK_COL_NUM * SIZE + 10, 10))
score_show_msg = font.render(str(score), True, (255, 255, 255))
screen.blit(score_show_msg, (BLOCK_COL_NUM * SIZE + 10, 50))
speed_show_msg = font.render('速度: ', True, (255, 255, 255))
screen.blit(speed_show_msg, (BLOCK_COL_NUM * SIZE + 10, 100))
speed_show_msg = font.render(speed_info, True, (255, 255, 255))
screen.blit(speed_show_msg, (BLOCK_COL_NUM * SIZE + 10, 150))
next_style_msg = font.render('下一個: ', True, (255, 255, 255))
screen.blit(next_style_msg, (BLOCK_COL_NUM * SIZE + 10, 200))
for row, line in enumerate(next_block):
for col, block in enumerate(line):
if block != '.':
pygame.draw.rect(screen, (20, 128, 200), (320 + SIZE * col, (BLOCK_COL_NUM + row) * SIZE, SIZE, SIZE), 0)
pygame.draw.line(screen, (0, 0, 0), (320 + SIZE * col, (BLOCK_COL_NUM + row) * SIZE), (320 + SIZE * col, (BLOCK_COL_NUM + row + 1) * SIZE), 1)
pygame.draw.line(screen, (0, 0, 0), (320 + SIZE * col, (BLOCK_COL_NUM + row) * SIZE), (320 + SIZE * (col + 1), (BLOCK_COL_NUM + row) * SIZE), 1)
pygame.draw.line(screen, (0, 0, 0), (320 + SIZE * col, (BLOCK_COL_NUM + row + 1) * SIZE), (320 + SIZE * (col + 1), (BLOCK_COL_NUM + row + 1) * SIZE), 1)
pygame.draw.line(screen, (0, 0, 0), (320 + SIZE * (col + 1), (BLOCK_COL_NUM + row) * SIZE), (320 + SIZE * (col + 1), (BLOCK_COL_NUM + row + 1) * SIZE), 1)
if game_over:
game_over_tips = game_over_font.render('GAME OVER', True, RED)
screen.blit(game_over_tips, ((SCREEN_WIDTH - game_over_font_width) // 2, (SCREEN_HEIGHT - game_over_font_height) // 2))
game_again = font.render('滑鼠點選任意位置,再來一局', True, RED)
screen.blit(game_again, ((SCREEN_WIDTH - game_again_font_width) // 2, (SCREEN_HEIGHT - game_again_font_height) // 2 + 80))
pygame.display.update()
clock.tick(60)
if __name__ == '__main__':
main()
```