沧州市网站建设_网站建设公司_H5网站_seo优化
2026/1/1 19:19:39 网站建设 项目流程

´´´python

!/usr/bin/env python3

import gi
gi.require_version('Gtk', '3.0')
gi.require_version('Gdk', '3.0')
from gi.repository import Gtk, Gdk, GLib
import cairo
import random
import sys
import json
import os

CELL = 25
W, H = 10, 20
BOARD_W = W * CELL
BOARD_H = H * CELL
SIDEBAR_W = 8 * CELL
next_time = 400

PIECES = [
[[1,1,1,1]],
[[1,1],[1,1]],
[[0,1,0],[1,1,1]],
[[0,1,1],[1,1,0]],
[[1,1,0],[0,1,1]],
[[1,0,0],[1,1,1]],
[[0,0,1],[1,1,1]]
]
COLORS = [ (0.9,0.2,0.2), (0.2,0.9,0.3), (0.95,0.85,0.2), (0.2,0.4,0.9), (0.85,0.3,0.9), (0.2,0.9,0.9), (0.6,0.6,0.6) ]

COLORS_BY_LEVEL = {
1: [
(0.8, 0.25, 0.45), # 方块0 - 红色→洋红色调
(0.25, 0.8, 0.5), # 方块1 - 绿色→青绿色调
(0.85, 0.75, 0.35), # 方块2 - 黄色→黄绿色调
(0.25, 0.5, 0.9), # 方块3 - 蓝色→保持蓝色调
(0.75, 0.35, 0.85), # 方块4 - 紫色→蓝紫色调
(0.25, 0.85, 0.85), # 方块5 - 青色→保持青色调
(0.65, 0.65, 0.7), # 方块6 - 灰色→冷灰色调
],
2: [
(0.75, 0.25, 0.55), # 方块0
(0.25, 0.75, 0.55), # 方块1
(0.8, 0.7, 0.4), # 方块2
(0.25, 0.55, 0.85), # 方块3
(0.7, 0.4, 0.8), # 方块4
(0.25, 0.8, 0.8), # 方块5
(0.6, 0.65, 0.75), # 方块6
],
3: [
(0.7, 0.25, 0.6), # 方块0 - 开始有明显冷色调偏移
(0.25, 0.7, 0.6), # 方块1
(0.75, 0.65, 0.45), # 方块2
(0.25, 0.6, 0.8), # 方块3
(0.65, 0.45, 0.75), # 方块4
(0.25, 0.75, 0.75), # 方块5
(0.55, 0.65, 0.8), # 方块6
],
4: [
(0.65, 0.25, 0.65), # 方块0 - 洋红色更明显
(0.25, 0.65, 0.65), # 方块1
(0.7, 0.6, 0.5), # 方块2
(0.25, 0.65, 0.75), # 方块3
(0.6, 0.5, 0.7), # 方块4
(0.25, 0.7, 0.7), # 方块5
(0.5, 0.65, 0.85), # 方块6
],
5: [
(0.6, 0.25, 0.7), # 方块0 - 开始有色违效果
(0.25, 0.6, 0.7), # 方块1
(0.65, 0.55, 0.55), # 方块2
(0.25, 0.7, 0.7), # 方块3
(0.55, 0.55, 0.65), # 方块4
(0.25, 0.65, 0.65), # 方块5
(0.45, 0.65, 0.9), # 方块6
],
6: [
(0.55, 0.25, 0.75), # 方块0 - 荧光效果开始
(0.25, 0.55, 0.75), # 方块1
(0.6, 0.5, 0.6), # 方块2
(0.25, 0.75, 0.65), # 方块3
(0.5, 0.6, 0.6), # 方块4
(0.25, 0.6, 0.6), # 方块5
(0.4, 0.65, 0.95), # 方块6
],
7: [
(0.5, 0.25, 0.8), # 方块0 - 明显的色违感
(0.25, 0.5, 0.8), # 方块1
(0.55, 0.45, 0.65), # 方块2
(0.25, 0.8, 0.6), # 方块3
(0.45, 0.65, 0.55), # 方块4
(0.25, 0.55, 0.55), # 方块5
(0.35, 0.65, 1.0), # 方块6
],
8: [
(0.45, 0.25, 0.85), # 方块0 - 霓虹色违效果
(0.25, 0.45, 0.85), # 方块1
(0.5, 0.4, 0.7), # 方块2
(0.25, 0.85, 0.55), # 方块3
(0.4, 0.7, 0.5), # 方块4
(0.25, 0.5, 0.5), # 方块5
(0.3, 0.65, 1.0), # 方块6
],
9: [
(0.4, 0.25, 0.9), # 方块0 - 最终级,强烈色违
(0.25, 0.4, 0.9), # 方块1
(0.45, 0.35, 0.75), # 方块2
(0.25, 0.9, 0.5), # 方块3 - 蓝绿色违
(0.5, 0.8, 0.8), # 方块4
(0.25, 0.45, 0.45), # 方块5
(0.25, 0.65, 1.0), # 方块6 - 亮蓝色
],
10: [
(1, 1, 1),
(1, 1, 1),
(1, 1, 1),
(1, 1, 1),
(1, 1, 1),
(1, 1, 1),
(1, 1, 1),
],
}

class Piece:
def init(self, id):
self.id = id
self.shape = [row[:] for row in PIECES[id]]
self.x = W//2 - len(self.shape[0])//2
self.y = -len(self.shape)
def rotate(self):
r = list(zip(*self.shape[::-1]))
self.shape = [list(row) for row in r]

class TetrisWindow(Gtk.Window):
def init(self):
super().init(title="Tetris")
self.set_default_size(BOARD_W + SIDEBAR_W + 40, BOARD_H + 40)
self.set_resizable(False)

	self.board = [[0]*W for _ in range(H)]self.score = 0# extended scoringself.total_blocks = 0	# total placed blocks over the gameself.combo = 0		# current consecutive clears (combo)self.last_cleared = 0	# last cleared lines count# level progressionself.level = 0self.clears_done = 0	# number of clear-events completed (not lines)self.level_speed_delta = 50	# milliseconds to speed up per level (easy to tweak)# visual effects stateself.particles = []self.floating_texts = []# effects timer id (for particles/floating texts when not animating)self._effects_timer_id = None# camera shakeself.shake_frames = 0self.shake_magnitude = 0# highscoreself.highscore = 0self._hs_path = os.path.join(os.path.dirname(__file__), 'tetris_highscore.json')self.load_highscore()self.paused = Falseself.cur = self.new_piece()self.next = self.new_piece()self.darea = Gtk.DrawingArea()self.darea.set_size_request(BOARD_W + SIDEBAR_W, BOARD_H)self.darea.connect('draw', self.on_draw)self.add(self.darea)self.connect('key-press-event', self.on_key)self.timeout_id = GLib.timeout_add(next_time, self.tick)def new_piece(self):return Piece(random.randrange(len(PIECES)))def game_over(self):# stop timertry:if self.timeout_id:GLib.source_remove(self.timeout_id)except Exception:pass# show dialog then quitdlg = Gtk.MessageDialog(parent=self, flags=0, message_type=Gtk.MessageType.INFO,buttons=Gtk.ButtonsType.OK, text="Game Over")dlg.format_secondary_text(f"Score: {self.score}")dlg.run()dlg.destroy()# update highscoreif self.score > self.highscore:self.highscore = self.scoreself.save_highscore()Gtk.main_quit()def spawn_next(self):# move next into current and create a new next; return False if game overself.cur = self.nextself.next = self.new_piece()# if any block of the new current collides or top row occupied -> game overif self.collide(self.cur):self.game_over()return False# also if top row has any block (stack reached top), consider game overif any(self.board[0]):self.game_over()return Falsereturn Truedef collide(self, piece):for i, row in enumerate(piece.shape):for j, v in enumerate(row):if not v: continuebx = piece.x + jby = piece.y + iif bx < 0 or bx >= W: return Trueif by >= H: return Trueif by >=0 and self.board[by][bx]: return Truereturn Falsedef place(self, piece):for i, row in enumerate(piece.shape):for j, v in enumerate(row):if not v: continuebx = piece.x + jby = piece.y + iif 0 <= by < H and 0 <= bx < W:self.board[by][bx] = piece.id + 1self.total_blocks += 1# small per-block placement scoreself.score += 1def clear_lines(self):# synchronous fallback: remove all full rows and return countnew_rows = [row[:] for row in self.board if not all(row)]cleared = H - len(new_rows)if cleared > 0:for _ in range(cleared):new_rows.insert(0, [0]*W)self.board = new_rowsreturn cleareddef detect_full_rows(self):rows = [i for i in range(H) if all(self.board[i])]return rowsdef remove_rows(self, rows):# remove rows (list of indices), keep ordernew_rows = [self.board[i] for i in range(H) if i not in set(rows)]# add empty rows on topfor _ in range(len(rows)):new_rows.insert(0, [0]*W)self.board = new_rowsdef animate_clear(self, rows, after_callback=None):# rows: list of indices to clear; animate a few flashes then removeif not rows:if after_callback: after_callback()returnself._anim_rows = set(rows)self._anim_phase = 0self._after_clear_callback = after_callbackself.animating = True# prepare particles for visual effect: one particle per cell in rowsself.particles = []for r in rows:for c in range(W):if self.board[r][c]:for i in range(10):# particle: x,y, vx, vy, life(0..1), start_color, end_color, sizepx = 10 + c*CELL + CELL/2py = 10 + r*CELL + CELL/2angle = random.uniform(0, 2*3.14159)speed = random.uniform(1.0,7.0)vx = speed * random.uniform(-1.0, 1.0)vy = -abs(random.uniform(1.0,8.0))life = 1.0col = COLORS[(self.board[r][c]-1) % len(COLORS)]# end color slightly fadedend_col = (min(1, col[0]+0.3), min(1, col[1]+0.3), min(1, col[2]+0.3))size = random.uniform(2.0,5.0)self.particles.append([px, py, vx, vy, life, col, end_col, size])# start an effects timer to animate particles/floating texts at higher frame rateself._start_effects_timer()# start timer: toggle flash every 120ms, do 6 phases (3 flashes)if hasattr(self, '_anim_timer_id') and self._anim_timer_id:GLib.source_remove(self._anim_timer_id)self._anim_timer_id = GLib.timeout_add(120, self._anim_tick)def _anim_tick(self):self._anim_phase += 1# queue redraw to show flashself.darea.queue_draw()# update particles/texts during animation at anim tick rateself._update_particles(anim=True)self._update_floating_texts(anim=True)# after 6 phases remove rowsif self._anim_phase >= 6:rows = sorted(self._anim_rows)# compute scoring before rows are removed (we still have the board cells)lines = len(rows)# compute number of cleared blockscleared_blocks = 0for r in rows:cleared_blocks += sum(1 for v in self.board[r] if v)# scoring: base by lines, bonus by cleared blocks, combo multiplierbonus = cleared_blocks * 10# combo: if previous clear >0 then increment combo else reset was handled earlierself.combo += 1 if lines>0 else 0combo_mul = 1 + (self.combo - 1) * 0.5 if self.combo>1 else 1score_gain = int((bonus) * combo_mul)self.score += score_gainself.last_cleared = lines# create floating text at center of cleared area (avoid stacking)minr, maxr = min(rows), max(rows)cx = 10 + (W*CELL)/2cy = 10 + ((minr + maxr + 1)/2.0) * CELL# avoid stacking: shift horizontally by current number of floating_textsshift = (len(self.floating_texts) % 5) * 12 - 24self.floating_texts.append([cx + shift, cy, f"+{score_gain}", 45])# progress level count per clear-eventself.clears_done += len(rows)# when clears_done reaches threshold, level upthreshold = 7 + self.level# threshold = 0if self.clears_done >= threshold:self.clears_done -= thresholdself.level += 1# self.level = 9k = min(9, self.level)# print(k)# k = 10global COLORSCOLORS = COLORS_BY_LEVEL[k]# print(COLORS)# speed up main tick timertry:if getattr(self, 'timeout_id', None):GLib.source_remove(self.timeout_id)except Exception:pass# reduce next_time by delta but keep a minimumglobal next_timeif self.level == 0: next_time = 400if self.level == 1: next_time = 350if self.level == 2: next_time = 300if self.level == 3: next_time = 260if self.level == 4: next_time = 220if self.level == 5: next_time = 180if self.level == 6: next_time = 140if self.level == 7: next_time = 100if self.level == 8: next_time = 85if self.level >= 9: next_time = 70# self.level_speed_delta = 0# print(next_time)self.timeout_id = GLib.timeout_add(next_time, self.tick)# camera shake proportional to linesself.shake_frames = 8self.shake_magnitude = min(12, 3 * lines)# then remove rowsself.remove_rows(rows)# cleanuptry:GLib.source_remove(self._anim_timer_id)except Exception:passself._anim_timer_id = Noneself._anim_rows = set()self._anim_phase = 0self.animating = False# redraw finalself.darea.queue_draw()if self._after_clear_callback:cb = self._after_clear_callbackself._after_clear_callback = Nonecb()return Falsereturn Truedef _update_particles(self, anim=False):# wrapper for compatibility: if anim True, run the same update; keep API# (we keep original signature for existing calls)newp = []wind = random.uniform(-0.15, 0.15)for p in self.particles:px, py, vx, vy, life, scol, ecol, size = pvx += wind * 0.3vy += 0.25px += vxpy += vylife -= 0.06 if anim else 0.04if life > 0:newp.append([px, py, vx, vy, life, scol, ecol, size])self.particles = newpdef load_highscore(self):try:if os.path.exists(self._hs_path):with open(self._hs_path, 'r') as f:data = json.load(f)self.highscore = int(data.get('highscore', 0))except Exception:self.highscore = 0def save_highscore(self):try:with open(self._hs_path, 'w') as f:json.dump({'highscore': int(self.highscore)}, f)except Exception:passdef _update_floating_texts(self, anim=False):# each text: x,y,text,lifenewt = []for t in self.floating_texts:x,y,txt,life = t# move up and fadey -= 1.2 if anim else 0.8life -= 2 if anim else 1if life>0:newt.append([x,y,txt,life])self.floating_texts = newtdef _start_effects_timer(self):if getattr(self, '_effects_timer_id', None):returnself._effects_timer_id = GLib.timeout_add(60, self._effects_tick)def _stop_effects_timer(self):if getattr(self, '_effects_timer_id', None):try:GLib.source_remove(self._effects_timer_id)except Exception:passself._effects_timer_id = Nonedef _effects_tick(self):# update particles and floating texts at higher frame rateself._update_particles(anim=False)self._update_floating_texts(anim=False)# stop timer if nothing leftif not self.particles and not self.floating_texts:self._effects_timer_id = Nonereturn Falseself.darea.queue_draw()return Truedef tick(self):# if animating, skip gravity and actions until animation finishesif getattr(self, 'animating', False):# still update animationsself._update_particles()self._update_floating_texts()return Trueif self.paused: return Trueself.cur.y += 1if self.collide(self.cur):self.cur.y -= 1self.place(self.cur)rows = self.detect_full_rows()if rows:# animate then continuedef after():lines = len(rows)# combo handled in anim tick; here just spawn nextself.combo = self.combo  # no-op placeholderif not self.spawn_next():returnself.animate_clear(rows, after_callback=after)else:# no lines cleared: reset comboself.combo = 0if not self.spawn_next():return Falseself.darea.queue_draw()return Truedef on_key(self, widget, event):key = Gdk.keyval_name(event.keyval)if key in ("q", "Q"):Gtk.main_quit()if key in ('p', 'P'):self.paused = not self.paused# if animating, ignore other keys except quit/pauseif getattr(self, 'animating', False):returnif self.paused: returnif key in ("Left", "a", "A"):self.cur.x -= 1if self.collide(self.cur): self.cur.x += 1elif key in ("Right", "d", "D"):self.cur.x += 1if self.collide(self.cur): self.cur.x -= 1elif key in ("Down", "s", "S"):self.cur.y += 1if self.collide(self.cur):self.cur.y -= 1self.place(self.cur)rows = self.detect_full_rows()if rows:def after():# spawn handled after animation; keep combo placeholderif not self.spawn_next():returnself.animate_clear(rows, after_callback=after)else:# no clear -> reset comboself.combo = 0if not self.spawn_next():returnelif key in ("Up","w","W"):old = [r[:] for r in self.cur.shape]self.cur.rotate()if self.collide(self.cur):self.cur.shape = oldelif key == 'space':while not self.collide(self.cur):self.cur.y += 1self.cur.y -= 1self.place(self.cur)rows = self.detect_full_rows()if rows:def after():if not self.spawn_next():returnself.animate_clear(rows, after_callback=after)else:# no clear -> reset comboself.combo = 0if not self.spawn_next():returnself.darea.queue_draw()def on_draw(self, widget, cr):# backgroundcr.set_source_rgb(0.1,0.1,0.1)cr.paint()# camera shake offsetoffx = 0offy = 0if self.shake_frames > 0:offx = random.uniform(-self.shake_magnitude, self.shake_magnitude)offy = random.uniform(-self.shake_magnitude, self.shake_magnitude)self.shake_frames -= 1# draw board background (apply camera offset)cr.set_source_rgb(0.0,0.0,0.0)cr.rectangle(10+offx,10+offy,BOARD_W, BOARD_H)cr.fill()# draw cellsfor i in range(H):for j in range(W):val = self.board[i][j]if val:# during animation, if this row is being flashed, toggle visibilityif hasattr(self, '_anim_rows') and i in getattr(self, '_anim_rows', set()):phase = getattr(self, '_anim_phase', 0)# flash on even phasesif phase % 2 == 0:k = (val-1) % len(COLORS)color = COLORS[(val-1) % len(COLORS)]# print(COLORS[k])cr.set_source_rgb(*color)cr.rectangle(10+offx + j*CELL, 10+offy + i*CELL, CELL-1, CELL-1)cr.fill()else:color = COLORS[(val-1) % len(COLORS)]cr.set_source_rgb(*color)cr.rectangle(10+offx + j*CELL, 10+offy + i*CELL, CELL-1, CELL-1)cr.fill()# draw current piecefor i,row in enumerate(self.cur.shape):for j,v in enumerate(row):if not v: continuebx = self.cur.x + jby = self.cur.y + iif by >=0:color = COLORS[self.cur.id % len(COLORS)]cr.set_source_rgb(*color)cr.rectangle(10+offx + bx*CELL, 10+offy + by*CELL, CELL-1, CELL-1)cr.fill()# sidebar: next piece & scoresx = 20 + BOARD_Wsy = 20cr.set_source_rgb(0.2,0.2,0.2)cr.rectangle(sx, 10, SIDEBAR_W-20, BOARD_H)cr.fill()cr.set_source_rgb(1,1,1)cr.select_font_face("Monospace", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD)cr.set_font_size(18)cr.move_to(sx+10, sy+20)cr.show_text(f"Score: {self.score}")cr.move_to(sx+10, sy+30)cr.set_font_size(9)cr.show_text(f"History: {self.highscore}")cr.move_to(sx+10, sy+50)cr.set_font_size(12)cr.show_text(f"Blocks: {self.total_blocks}")cr.move_to(sx+10, sy+70)cr.show_text(f"Combo: x{1 + max(0,self.combo-1)*0.5:.1f}")cr.move_to(sx+10, sy+90)cr.show_text(f"Level: {self.level}")cr.move_to(sx+10, sy+110)cr.show_text(f"Clears: {self.clears_done}/{7 + self.level}")cr.move_to(sx+10, sy+130)cr.show_text("Next:")# draw next piecefor i,row in enumerate(self.next.shape):for j,v in enumerate(row):if not v: continuecolor = COLORS[self.next.id % len(COLORS)]cr.set_source_rgb(*color)cr.rectangle(sx + 10 + j*CELL, sy + 140 + i*CELL, CELL-1, CELL-1)cr.fill()# draw particles (interpolate color by life)for p in self.particles:px, py, vx, vy, life, scol, ecol, size = palpha = max(0, min(1, life))# linear interpolation of colorr = scol[0]*life + ecol[0]*(1-life)g = scol[1]*life + ecol[1]*(1-life)b = scol[2]*life + ecol[2]*(1-life)cr.set_source_rgba(r, g, b, alpha)cr.arc(px+offx, py+offy, size, 0, 2*3.14159)cr.fill()# draw floating textscr.set_font_size(18)for t in self.floating_texts:x,y,txt,life = talpha = max(0, min(1, life/45.0))cr.set_source_rgba(1,1,1, alpha)cr.move_to(x+offx, y+offy)cr.show_text(txt)

if name == 'main':
win = TetrisWindow()
win.connect('destroy', Gtk.main_quit)
win.show_all()
Gtk.main()

´´´

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询