import pygame
import random
pygame.init()
# ========= サイズ =========
WIDTH, HEIGHT = 20, 40
CELL = 24
HALF = CELL // 2
screen = pygame.display.set_mode((WIDTH * CELL, HEIGHT * CELL))
clock = pygame.time.Clock()
# ========= SRS キック =========
JLSTZ_KICKS = {
(0,1): [(0,0),(-1,0),(-1,1),(0,-2),(-1,-2)],
(1,0): [(0,0),(1,0),(1,-1),(0,2),(1,2)],
(1,2): [(0,0),(1,0),(1,-1),(0,2),(1,2)],
(2,1): [(0,0),(-1,0),(-1,1),(0,-2),(-1,-2)],
(2,3): [(0,0),(1,0),(1,1),(0,-2),(1,-2)],
(3,2): [(0,0),(-1,0),(-1,-1),(0,2),(-1,2)],
(3,0): [(0,0),(-1,0),(-1,-1),(0,2),(-1,2)],
(0,3): [(0,0),(1,0),(1,1),(0,-2),(1,-2)],
}
I_KICKS = {
(0,1): [(0,0),(-2,0),(1,0),(-2,-1),(1,2)],
(1,0): [(0,0),(2,0),(-1,0),(2,1),(-1,-2)],
(1,2): [(0,0),(-1,0),(2,0),(-1,2),(2,-1)],
(2,1): [(0,0),(1,0),(-2,0),(1,-2),(-2,1)],
(2,3): [(0,0),(2,0),(-1,0),(2,1),(-1,-2)],
(3,2): [(0,0),(-2,0),(1,0),(-2,-1),(1,2)],
(3,0): [(0,0),(1,0),(-2,0),(1,-2),(-2,1)],
(0,3): [(0,0),(-1,0),(2,0),(-1,2),(2,-1)],
}
# ========= テトリミノ =========
PIECES = {
"I": {
0: [(0,1),(1,1),(2,1),(3,1)],
1: [(2,0),(2,1),(2,2),(2,3)],
2: [(0,2),(1,2),(2,2),(3,2)],
3: [(1,0),(1,1),(1,2),(1,3)],
},
"O": {
0: [(1,0),(2,0),(1,1),(2,1)],
1: [(1,0),(2,0),(1,1),(2,1)],
2: [(1,0),(2,0),(1,1),(2,1)],
3: [(1,0),(2,0),(1,1),(2,1)],
},
"T": {
0: [(1,0),(0,1),(1,1),(2,1)],
1: [(1,0),(1,1),(2,1),(1,2)],
2: [(0,1),(1,1),(2,1),(1,2)],
3: [(1,0),(0,1),(1,1),(1,2)],
},
"S": {
0: [(1,0),(2,0),(0,1),(1,1)],
1: [(1,0),(1,1),(2,1),(2,2)],
2: [(1,1),(2,1),(0,2),(1,2)],
3: [(0,0),(0,1),(1,1),(1,2)],
},
"Z": {
0: [(0,0),(1,0),(1,1),(2,1)],
1: [(2,0),(1,1),(2,1),(1,2)],
2: [(0,1),(1,1),(1,2),(2,2)],
3: [(1,0),(0,1),(1,1),(0,2)],
},
"J": {
0: [(0,0),(0,1),(1,1),(2,1)],
1: [(1,0),(2,0),(1,1),(1,2)],
2: [(0,1),(1,1),(2,1),(2,2)],
3: [(1,0),(1,1),(0,2),(1,2)],
},
"L": {
0: [(2,0),(0,1),(1,1),(2,1)],
1: [(1,0),(1,1),(1,2),(2,2)],
2: [(0,1),(1,1),(2,1),(0,2)],
3: [(0,0),(1,0),(1,1),(1,2)],
},
}
# ========= ペンタミノ 0° 定義(ここから自動で 4 回転生成) =========
PENTA_BASE = {
"P5": [(0,0),(1,0),(0,1),(1,1),(0,2)],
"P5U": [(0,0),(1,0),(0,1),(1,1),(1,2)],
"X5": [(1,0),(0,1),(1,1),(2,1),(1,2)],
"T5": [(1,0),(0,1),(1,1),(2,1),(1,2)],
"U5": [(0,0),(2,0),(0,1),(1,1),(2,1)],
}
def gen_rotations(shape):
rots = {}
cur = shape
for r in range(4):
min_x = min(x for x, y in cur)
min_y = min(y for x, y in cur)
norm = [(x - min_x, y - min_y) for x, y in cur]
rots[r] = norm
nxt = []
for x, y in cur:
nx = -y
ny = x
nxt.append((nx, ny))
cur = nxt
return rots
for name, base in PENTA_BASE.items():
PIECES[name] = gen_rotations(base)
# ========= 盤面 =========
board = [[0]*WIDTH for _ in range(HEIGHT)]
# ========= 基本関数 =========
def can_place_blocks(blocks, x, y):
for dx, dy in blocks:
nx, ny = x + dx, y + dy
if nx < 0 or nx >= WIDTH or ny < 0 or ny >= HEIGHT:
return False
if board[ny][nx] == 1:
return False
return True
def place_blocks(blocks, x, y):
for dx, dy in blocks:
nx, ny = x + dx, y + dy
board[ny][nx] = 1
def draw(active_blocks, ax, ay, hide=None, falling=None):
screen.fill((0,0,0))
for yy in range(HEIGHT):
for xx in range(WIDTH):
if board[yy][xx] == 1:
if hide and (xx,yy) in hide:
continue
pygame.draw.rect(screen, (0,200,200),
(xx*CELL, yy*CELL, CELL-1, CELL-1))
if falling:
for fx, fy in falling:
pygame.draw.rect(screen, (255,80,80),
(fx*CELL, fy, CELL-1, CELL-1))
if active_blocks:
for dx, dy in active_blocks:
pygame.draw.rect(screen, (200,200,0),
((ax+dx)*CELL, (ay+dy)*CELL, CELL-1, CELL-1))
pygame.display.flip()
# ========= 回転 =========
def rotate_srs(piece_type, rot, x, y):
new_rot = (rot + 1) % 4
blocks_new = PIECES[piece_type][new_rot]
if piece_type == "O":
if can_place_blocks(blocks_new, x, y):
return new_rot, x, y
return rot, x, y
if piece_type in ("I","J","L","S","T","Z"):
kicks = I_KICKS if piece_type == "I" else JLSTZ_KICKS
for ox, oy in kicks.get((rot, new_rot), [(0,0)]):
nx, ny = x + ox, y + oy
if can_place_blocks(blocks_new, nx, ny):
return new_rot, nx, ny
return rot, x, y
if piece_type in ("P5","P5U","X5","T5","U5"):
SIMPLE_KICKS = [
(0,0),(1,0),(-1,0),(0,-1),(0,1),
(1,-1),(-1,-1),(1,1),(-1,1)
]
for ox, oy in SIMPLE_KICKS:
nx, ny = x + ox, y + oy
if can_place_blocks(blocks_new, nx, ny):
return new_rot, nx, ny
return rot, x, y
# ========= 行消し =========
def clear_lines_once():
new_board = []
cleared = []
for y in range(HEIGHT):
if all(board[y]):
cleared.append(y)
else:
new_board.append(board[y])
if not cleared:
return []
while len(new_board) < HEIGHT:
new_board.insert(0, [0]*WIDTH)
for y in range(HEIGHT):
board[y] = new_board[y]
return cleared
# ========= 縦穴落下アニメーション =========
def cascade_animation(cleared, active_blocks, ax, ay):
if not cleared:
return
bottom = max(cleared)
target = bottom + 1
if target >= HEIGHT:
return
fall_cols = []
segments = {}
for x in range(WIDTH):
if board[target][x] != 0:
continue
ys = []
for y in range(target-1, -1, -1):
if board[y][x] == 1:
ys.append(y)
else:
break
if 1 <= len(ys) <= 4:
ys.sort()
fall_cols.append(x)
segments[x] = ys
if not fall_cols:
return
fall_info = {}
hide = set()
for x in fall_cols:
ys = segments[x]
for y in ys:
hide.add((x,y))
y_bottom = ys[-1]
stop_row = HEIGHT - 1
for yy in range(y_bottom+1, HEIGHT):
if board[yy][x] == 1:
stop_row = yy - 1
break
h = len(ys)
final_rows = list(range(stop_row - h + 1, stop_row + 1))
fall_info[x] = []
for y, fr in zip(ys, final_rows):
fall_info[x].append({
"x": x,
"start": y*CELL,
"cur": y*CELL,
"end": fr*CELL
})
anim = True
while anim:
anim = False
falling_blocks = []
pygame.event.pump()
for x in fall_cols:
for b in fall_info[x]:
if b["cur"] < b["end"]:
b["cur"] = min(b["cur"] + HALF, b["end"])
anim = True
falling_blocks.append((b["x"], b["cur"]))
draw(None, 0, 0, hide=hide, falling=falling_blocks)
pygame.time.delay(50)
for x in fall_cols:
ys = segments[x]
for y in ys:
board[y][x] = 0
for b in fall_info[x]:
final_row = b["end"] // CELL
board[final_row][x] = 1
draw(None, 0, 0)
pygame.time.delay(80)
# ========= せり上がり =========
def rise_shift_animation():
global rise_hole_x, rise_dir, rise_same_count
new_row = [1] * WIDTH
new_row[rise_hole_x] = 0
for y in range(HEIGHT - 1):
board[y] = board[y + 1][:]
board[HEIGHT - 1] = new_row
rise_same_count += 1
if rise_same_count >= 5:
rise_same_count = 0
rise_hole_x += rise_dir
if rise_hole_x <= 0:
rise_hole_x = 0
rise_dir = 1
elif rise_hole_x >= WIDTH - 1:
rise_hole_x = WIDTH - 1
rise_dir = -1
draw(None, 0, 0)
pygame.time.delay(120)
# ========= 行消し後の処理 =========
def resolve_all(active_blocks, ax, ay):
while True:
cleared = clear_lines_once()
if not cleared:
break
draw(None, 0, 0)
pygame.time.delay(150)
cascade_animation(cleared, active_blocks, ax, ay)
rise_shift_animation()
# ========= 初期 5 行の地面 =========
def init_ground_rows():
global rise_hole_x, rise_dir, rise_same_count
rise_hole_x = WIDTH // 2
rise_dir = 1
rise_same_count = 5
for i in range(5):
new_row = [1] * WIDTH
new_row[rise_hole_x] = 0
board[HEIGHT - 1 - i] = new_row[:]
# ========= 新しいミノ =========
def new_piece():
ptype = random.choice(list(PIECES.keys()))
rot = 0
x = WIDTH // 2 - 2
y = 0
return ptype, rot, x, y
# ========= 初期化 =========
init_ground_rows()
piece_type, rot, x_pos, y_pos = new_piece()
fall_timer = 0
move_cd = 0
rot_cd = 0
# ========= メインループ =========
running = True
while running:
clock.tick(60)
fall_timer += 1
move_cd = max(0, move_cd - 1)
rot_cd = max(0, rot_cd - 1)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
active_blocks = PIECES[piece_type][rot]
keys = pygame.key.get_pressed()
# --- 左右移動 ---
if move_cd == 0:
if keys[pygame.K_LEFT] and can_place_blocks(active_blocks, x_pos - 1, y_pos):
x_pos -= 1
move_cd = 8
if keys[pygame.K_RIGHT] and can_place_blocks(active_blocks, x_pos + 1, y_pos):
x_pos += 1
move_cd = 8
# --- 回転 ---
if rot_cd == 0 and (keys[pygame.K_UP] or keys[pygame.K_SPACE]):
new_rot, nx, ny = rotate_srs(piece_type, rot, x_pos, y_pos)
rot, x_pos, y_pos = new_rot, nx, ny
rot_cd = 12
# --- 下移動(ソフトドロップ) ---
if keys[pygame.K_DOWN] and can_place_blocks(active_blocks, x_pos, y_pos + 1):
y_pos += 1
# --- 自動落下 ---
if fall_timer >= 30:
fall_timer = 0
if can_place_blocks(active_blocks, x_pos, y_pos + 1):
y_pos += 1
else:
place_blocks(active_blocks, x_pos, y_pos)
resolve_all(active_blocks, x_pos, y_pos)
piece_type, rot, x_pos, y_pos = new_piece()
if not can_place_blocks(PIECES[piece_type][rot], x_pos, y_pos):
print("GAME OVER")
running = False
draw(PIECES[piece_type][rot], x_pos, y_pos)
pygame.quit()
watatata
In Japanese.
touch
Sunday, March 1, 2026
AIサンプル
Saturday, February 28, 2026
Wednesday, February 4, 2026
Subscribe to:
Comments (Atom)