在之前工作基础上(Tauri(2.5.1)+Leptos(0.8.2)开发自用桌面小程序-CSDN博客),继续进行自用桌面小程序的开发,这次完全使用DeepSeek辅助编程做一个俄罗斯方块游戏,大部分代码由DeepSeek自主完成,Bug扔给DeepSeek自行处理,期间人为简单干预即可。具体游戏界面如下:
1. DeepSeek辅助编程界面
Win10的操作系统,使用VS Code及Rust analyzer插件搭建的Rust开发环境,使用Roo Code绑定DeepSeek API ,配置比较简单,网上教程很多。
2. 页面设置
还是使用leptos-router新建一个页面(类似浏览器的标签页),用于俄罗斯方块游戏界面。主要在src/app/app.rs文件中设置,具体代码如下:
#[warn(unused_imports)]
use leptos::prelude::*;
use leptos_router::components::{Route, Router, Routes};
use leptos_router::path;
mod acidinput;
mod schedule;
mod game2048;
mod game5;
mod match_game;
use acidinput::*;
use schedule::*;
use game2048::*;
use game5::*;
use match_game::*;
#[component]
pub fn App() -> impl IntoView {
view! {
<Router>
<nav>
<a class="nav" href="/">"工作进度表"</a>
<a class="nav" href="/acidinput">"产品录入"</a>
<a class="nav" href="/game2048">"2048数字游戏"</a>
<a class="nav" href="/game5">"五子棋游戏"</a>
<a class="nav" href="/matchgame">"俄罗斯方块"</a>
</nav>
<main>
<Routes fallback=|| "Not found.">
// / just has an un-nested "Home"
<Route path=path!("/") view= || view! {<WorkSchedule />} />
<Route path=path!("/acidinput") view=|| view! {<AcidInput />} />
<Route path=path!("/game2048") view=|| view! {<GameBoard />} />
<Route path=path!("/game5") view=|| view! {<GomokuGame />} />
<Route path=path!("/matchgame") view=|| view! {<TetrisGameBoard />} />
</Routes>
</main>
</Router>
}
}
3. 游戏主程序
俄罗斯方块游戏的代码被放在了文件src/app/match_game.rs中,绝大部分代码和注释都是DeepSeek完成的,具体代码如下:
use leptos::*;
use leptos::prelude::*;
use leptos::component;
use leptos::view;
use wasm_bindgen::prelude::*;
use web_sys;
use leptos::task::spawn_local;
use std::rc::Rc;
use std::cell::RefCell;
use wasm_bindgen::prelude::Closure;
use leptos::logging::log;
/// 方块形状枚举
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum Tetromino {
I, // I形
J, // J形
L, // L形
O, // O形
S, // S形
T, // T形
Z, // Z形
}
/// 游戏方向枚举
#[derive(Clone, Copy, PartialEq)]
pub enum Direction {
Left,
Right,
Down,
}
/// 游戏状态结构体
#[derive(Clone)]
pub struct Game {
pub grid: [[Option<Tetromino>; 10]; 20], // 10x20游戏网格
pub current_piece: (Tetromino, [[i32; 2]; 4]), // 当前方块及其位置
pub next_piece: (Tetromino, u8), // 下一个方块及其旋转状态
pub score: u32, // 当前得分
pub level: u32, // 当前等级
pub game_over: bool, // 游戏是否结束
pub paused: bool, // 游戏是否暂停
pub is_locked: bool, // 方块是否已锁定
pub current_rotation: u8, // 当前旋转状态(0-3)
}
impl Game {
/// 创建新游戏实例
pub fn new() -> Self {
let mut game = Game {
grid: [[None; 10]; 20],
current_piece: (Tetromino::I, [[0; 2]; 4]),
next_piece: (Self::random_tetromino(), (rand::random::<f32>() * 4.0).floor() as u8),
score: 0,
level: 1,
game_over: false,
paused: false,
is_locked: false,
current_rotation: 0,
};
game.spawn_piece();
game
}
/// 随机生成方块
fn random_tetromino() -> Tetromino {
use rand::random;
let piece = match (random::<f64>() * 7.0).floor() as u8 {
0 => Tetromino::I,
1 => Tetromino::J,
2 => Tetromino::L,
3 => Tetromino::O,
4 => Tetromino::S,
5 => Tetromino::T,
_ => Tetromino::Z,
};
log!("Generated new piece: {:?}", piece);
piece
}
/// 生成新方块
fn spawn_piece(&mut self) {
//log!("[SPAWN] Current state - next_piece: {:?}, current_piece: {:?}",
// self.next_piece, self.current_piece.0);
// 保存当前预览方块及其旋转状态
let (current_falling, preview_rotation) = self.next_piece;
let actual_rotation = preview_rotation % 4;
//log!("[SPAWN] Will spawn: {:?} with preview_rotation {:?} (actual: {:?})",
// current_falling, preview_rotation, actual_rotation);
// 验证旋转状态一致性
//log!("[ROTATION VERIFY] Preview rotation: {}, Actual rotation: {}",
// preview_rotation, actual_rotation);
// 确保预览和实际方块类型一致
//log!("[SPAWN] Verifying piece types - preview: {:?}, actual: {:?}",
// current_falling, self.next_piece.0);
// 设置当前方块状态
self.current_piece.0 = current_falling;
self.is_locked = false;
self.current_rotation = actual_rotation; // 确保旋转状态同步
// 使用与预览完全相同的旋转状态计算方式
let positions = match (current_falling, actual_rotation) {
// 调整初始位置,确保最下端在第0行,其他部分可以在第0行以上
(Tetromino::I, 0) => [[-2, 3], [-2, 4], [-2, 5], [-2, 6]], // I型水平
(Tetromino::I, 1) => [[-3, 4], [-2, 4], [-1, 4], [0, 4]], // I型垂直
(Tetromino::I, 2) => [[-2, 3], [-2, 4], [-2, 5], [-2, 6]], // I型水平(反向)
(Tetromino::I, 3) => [[-3, 4], [-2, 4], [-1, 4], [0, 4]], // I型垂直(反向)
(Tetromino::O, _) => [[-1, 4], [-1, 5], [0, 4], [0, 5]], // O型
(Tetromino::J, 0) => [[-1, 4], [0, 4], [0, 5], [0, 6]], // J型初始
(Tetromino::J, 1) => [[-2, 5], [-2, 6], [-1, 5], [0, 5]], // J型90度
(Tetromino::J, 2) => [[-1, 4], [-1, 5], [-1, 6], [0, 6]], // J型180度
(Tetromino::J, 3) => [[-2, 5], [-1, 5], [0, 5], [0, 4]], // J型270度
(Tetromino::L, 0) => [[-1, 6], [0, 4], [0, 5], [0, 6]], // L型初始
(Tetromino::L, 1) => [[-2, 4], [-1, 4], [0, 4], [0, 5]], // L型90度
(Tetromino::L, 2) => [[-1, 4], [-1, 5], [-1, 6], [0, 4]], // L型180度
(Tetromino::L, 3) => [[-2, 4], [-2, 5], [-1, 5], [0, 5]], // L型270度
(Tetromino::S, 0) => [[-1, 5], [-1, 6], [0, 4], [0, 5]], // S型初始(右凸)
(Tetromino::S, 1) => [[-2, 5], [-1, 5], [-1, 6], [0, 6]], // S型90度(左凸)
(Tetromino::S, 2) => [[-1, 5], [-1, 6], [0, 4], [0, 5]], // S型180度(右凸)
(Tetromino::S, 3) => [[-2, 4], [-1, 4], [-1, 5], [0, 5]], // S型270度(左凸)
(Tetromino::T, 0) => [[-1, 5], [0, 4], [0, 5], [0, 6]], // T型初始
(Tetromino::T, 1) => [[-2, 5], [-1, 5], [-1, 6], [0, 5]], // T型90度
(Tetromino::T, 2) => [[-1, 4], [-1, 5], [-1, 6], [0, 5]], // T型180度
(Tetromino::T, 3) => [[-2, 5], [-1, 4], [-1, 5], [0, 5]], // T型270度
(Tetromino::Z, 0) => [[-1, 4], [-1, 5], [0, 5], [0, 6]], // Z型初始
(Tetromino::Z, 1) => [[-2, 6], [-1, 5], [-1, 6], [0, 5]], // Z型90度
(Tetromino::Z, 2) => [[-1, 4], [-1, 5], [0, 5], [0, 6]], // Z型180度
(Tetromino::Z, 3) => [[-2, 5], [-1, 4], [-1, 5], [0, 4]], // Z型270度
_ => unreachable!("Invalid rotation state"),
};
//log!("[POSITION] Final positions for {:?} with rotation {}: {:?}",
// current_falling, actual_rotation, positions);
//log!("[POSITION] Calculated positions for {:?} with rotation {}: {:?}",
// current_falling, actual_rotation, positions);
self.current_piece.1 = positions;
// 生成新预览方块及随机旋转状态(0-3)
let new_piece = Self::random_tetromino();
let new_rotation = (rand::random::<f32>() * 4.0).floor() as u8;
let new_preview = (new_piece, new_rotation);
//log!("[SPAWN] Generated new preview: {:?} with rotation {} (will be next piece)",
// new_preview.0, new_preview.1);
// 双重验证预览方块类型
if new_preview.0 != current_falling {
log!("[SPAWN] Preview and current piece match verified");
}
// 更新预览方块
self.next_piece = new_preview;
//log!("[SPAWN] Updated next_piece to: {:?}", self.next_piece);
//log!("[SPAWN] After update - next_piece: {:?}, current_piece: {:?}",
// self.next_piece, self.current_piece.0);
// 检查新方块位置是否有效(允许部分超出顶部)
let dropped_pos = self.get_dropped_position();
if !self.is_valid_position(&dropped_pos) || dropped_pos == self.current_piece.1 {
self.lock_piece();
} else {
// 检查新方块位置是否有效(允许部分超出顶部)
let mut valid_spawn = true;
for &[i, j] in &self.current_piece.1 {
if j < 0 || j >= 10 || i >= 20 || (i >= 0 && self.grid[i as usize][j as usize].is_some()) {
valid_spawn = false;
break;
}
}
self.game_over = !valid_spawn;
}
}
/// 检查方块是否完全无法移动
fn is_piece_stuck(&self) -> bool {
// 检查方块是否无法继续下落
for &[i, j] in &self.current_piece.1 {
// 如果方块在网格内(i >= 0)
if i >= 0 {
// 检查是否到达底部或下方有方块
if i >= 19 || self.grid[(i + 1) as usize][j as usize].is_some() {
return true;
}
}
}
false
}
/// 移动方块
pub fn move_piece(&mut self, direction: Direction) {
if self.game_over || self.paused || self.is_locked {
return;
}
let mut new_positions = self.current_piece.1;
// 计算新位置
for pos in &mut new_positions {
match direction {
Direction::Left => pos[1] -= 1,
Direction::Right => pos[1] += 1,
Direction::Down => pos[0] += 1,
}
}
// 检查新位置是否有效
if self.is_valid_position(&new_positions) {
self.current_piece.1 = new_positions;
// 如果是向下移动
if direction == Direction::Down {
// 检查是否已经到底部
if self.is_piece_stuck() {
self.lock_piece();
return; // 锁定后立即返回,防止后续移动
}
} else if self.is_piece_stuck() {
// 其他方向移动时检查是否卡住
self.lock_piece();
return; // 锁定后立即返回,防止后续移动
}
} else if direction == Direction::Down && self.is_piece_stuck() {
// 如果向下移动无效且方块卡住,也锁定
self.lock_piece();
}
}
/// 旋转方块
pub fn rotate_piece(&mut self) {
if self.game_over || self.paused || self.current_piece.0 == Tetromino::O {
return; // O方块不需要旋转
}
// 获取当前旋转状态(0-3)
let current_rot = self.current_rotation;
//log!("[ROTATE] Rotating {:?} from state {}", self.current_piece.0, current_rot);
// 根据方块类型定义精确旋转中心(取第二和第三个方块中间)
let (center_x, center_y) = match self.current_piece.0 {
Tetromino::I => (
(self.current_piece.1[1][0] + self.current_piece.1[2][0]) / 2,
(self.current_piece.1[1][1] + self.current_piece.1[2][1]) / 2
),
Tetromino::O => (self.current_piece.1[0][0] + 1, self.current_piece.1[0][1] + 1), // O型中心在四个方块中间
Tetromino::J => (
(self.current_piece.1[1][0] + self.current_piece.1[2][0]) / 2,
(self.current_piece.1[1][1] + self.current_piece.1[2][1]) / 2
),
Tetromino::L => (
(self.current_piece.1[1][0] + self.current_piece.1[2][0]) / 2,
(self.current_piece.1[1][1] + self.current_piece.1[2][1]) / 2
),
Tetromino::S => (
(self.current_piece.1[1][0] + self.current_piece.1[2][0]) / 2,
(self.current_piece.1[1][1] + self.current_piece.1[2][1]) / 2
),
Tetromino::Z => (
(self.current_piece.1[1][0] + self.current_piece.1[2][0]) / 2,
(self.current_piece.1[1][1] + self.current_piece.1[2][1]) / 2
),
Tetromino::T => (self.current_piece.1[1][0], self.current_piece.1[1][1]), // T型中心取第二个方块
};
//log!("[ROTATE] Fixed center for {:?}: ({}, {})", self.current_piece.0, center_x, center_y);
let mut new_positions = self.current_piece.1.clone();
// 应用旋转
for pos in &mut new_positions {
// 计算相对于中心的坐标
let x = pos[1] - center_y;
let y = pos[0] - center_x;
// 应用标准90度顺时针旋转矩阵
let new_x = -y;
let new_y = x;
// 计算新位置
pos[1] = center_y + new_x;
pos[0] = center_x + new_y;
}
// 尝试原始位置
if self.is_valid_position(&new_positions) {
self.apply_rotation(new_positions, current_rot);
return;
}
// 墙踢: 尝试左右移动1-2格
for offset in [1, -1, 2, -2].iter() {
let mut kicked_positions = new_positions.clone();
for pos in &mut kicked_positions {
pos[1] += offset;
}
if self.is_valid_position(&kicked_positions) {
self.apply_rotation(kicked_positions, current_rot);
//log!("[ROTATE] Applied wall kick with offset {}", offset);
return;
}
}
//log!("[ROTATE] Rotation failed - all positions invalid");
}
/// 检查位置是否有效
fn is_valid_position(&self, positions: &[[i32; 2]; 4]) -> bool {
for &[i, j] in positions {
// 允许i<0(顶部以上),只要j在有效范围内
// 仅检查网格内(i>=0)的方块重叠
if j < 0 || j >= 10 || i >= 20 || (i >= 0 && self.grid[i as usize][j as usize].is_some()) {
return false;
}
}
true
}
/// 获取下落到底部的位置
fn get_dropped_position(&self) -> [[i32; 2]; 4] {
let mut dropped = self.current_piece.1;
loop {
for pos in &mut dropped {
pos[0] += 1;
}
if !self.is_valid_position(&dropped) {
for pos in &mut dropped {
pos[0] -= 1;
}
break;
}
}
dropped
}
/// 硬降(直接下落到底部)
pub fn hard_drop(&mut self) {
if self.game_over || self.paused {
return;
}
self.current_piece.1 = self.get_dropped_position();
self.lock_piece();
}
/// 固定当前方块到网格
fn lock_piece(&mut self) {
if self.is_locked || self.game_over {
return;
}
let piece_type = self.current_piece.0;
for &[i, j] in &self.current_piece.1 {
if i >= 0 {
self.grid[i as usize][j as usize] = Some(piece_type);
// 如果方块被锁定在第0行,游戏结束
if i == 0 {
self.game_over = true;
}
}
}
self.is_locked = true;
self.clear_lines();
if !self.game_over {
self.spawn_piece();
}
}
/// 应用旋转并更新状态(内部方法)
pub(crate) fn apply_rotation(&mut self, positions: [[i32; 2]; 4], current_rot: u8) {
self.current_piece.1 = positions;
self.current_rotation = (current_rot + 1) % 4;
//log!("[ROTATE] Success! New state: {}", self.current_rotation);
//log!("[ROTATE] New positions: {:?}", self.current_piece.1);
// 旋转后检查是否卡住
if self.is_piece_stuck() {
self.lock_piece();
}
}
/// 清除完整的行
fn clear_lines(&mut self) {
let mut lines_cleared = 0;
// 从下往上扫描
let mut row = 19;
while row > 0 {
if self.grid[row].iter().all(|cell| cell.is_some()) {
lines_cleared += 1;
// 将上方行下移
for move_row in (1..=row).rev() {
self.grid[move_row] = self.grid[move_row - 1];
}
// 清空最顶行
self.grid[0] = [None; 10];
// 继续检查当前行(因为上方行已经下移)
continue;
}
row -= 1;
}
// 更新分数
match lines_cleared {
1 => self.score += 100 * self.level,
2 => self.score += 300 * self.level,
3 => self.score += 500 * self.level,
4 => self.score += 800 * self.level,
_ => (),
}
// 更新等级
if lines_cleared > 0 {
self.level = (self.score / 2000) + 1;
}
}
/// 暂停/继续游戏
pub fn toggle_pause(&mut self) {
if !self.game_over {
self.paused = !self.paused;
}
}
}
/// 游戏界面组件
#[component]
pub fn TetrisGameBoard() -> impl IntoView {
// 创建游戏状态信号
let (game, set_game) = signal(Game::new());
// 设置游戏循环(自动下落)
let tick = move || {
set_game.update(|g| {
if !g.game_over && !g.paused {
g.move_piece(Direction::Down);
}
});
};
// 每500ms触发一次下落(速度随等级提高)
let tick_interval = move || {
500.0 / (game.get_untracked().level as f64).max(1.0)
};
// 使用Leptos的spawn_local和web_sys的set_timeout实现游戏循环
spawn_local(async move {
let window = web_sys::window().expect("no global window exists");
let closure: Rc<RefCell<Option<Closure<dyn FnMut()>>>> = Rc::new(RefCell::new(None));
let closure_clone = Rc::clone(&closure);
*closure_clone.borrow_mut() = Some(Closure::<dyn FnMut()>::new({
let closure_clone = Rc::clone(&closure_clone);
let window = window.clone();
move || {
if !game.get_untracked().game_over && !game.get_untracked().paused {
tick();
}
let interval = tick_interval();
window.set_timeout_with_callback_and_timeout_and_arguments_0(
closure_clone.borrow().as_ref().unwrap().as_ref().unchecked_ref(),
interval as i32
).expect("failed to set timeout");
}
}));
// 初始调用
window.set_timeout_with_callback_and_timeout_and_arguments_0(
closure.borrow().as_ref().unwrap().as_ref().unchecked_ref(),
tick_interval() as i32
).expect("failed to set timeout");
});
// 监听键盘事件
let key_listener = window_event_listener(ev::keydown, move |ev| {
if game.get().game_over {
return;
}
ev.prevent_default();
match &ev.key()[..] {
"ArrowLeft" => set_game.update(|g| g.move_piece(Direction::Left)),
"ArrowRight" => set_game.update(|g| g.move_piece(Direction::Right)),
"ArrowDown" => set_game.update(|g| g.move_piece(Direction::Down)),
"ArrowUp" => set_game.update(|g| g.rotate_piece()),
" " => {
if !game.get().paused {
set_game.update(|g| g.hard_drop());
}
},
"p" | "P" => set_game.update(|g| g.toggle_pause()),
_ => (),
}
});
// 组件卸载时清理事件监听器和闭包
on_cleanup(move || {
key_listener.remove();
// 闭包会在离开作用域时自动释放
});
// 根据方块类型返回对应的CSS颜色类
fn tile_color(tile: Option<Tetromino>) -> &'static str {
if let Some(t) = tile {
match t {
Tetromino::I => "tetromino-i",
Tetromino::J => "tetromino-j",
Tetromino::L => "tetromino-l",
Tetromino::O => "tetromino-o",
Tetromino::S => "tetromino-s",
Tetromino::T => "tetromino-t",
Tetromino::Z => "tetromino-z",
}
} else {
"tetromino-empty"
}
}
// 渲染预览方块
fn render_preview((tetromino, rotation): (Tetromino, u8)) -> impl IntoView {
// 使用与实际方块相同的旋转状态
let rotation_mod = rotation % 4;
//log!("[PREVIEW] Rendering {:?} with rotation {}", tetromino, rotation_mod);
// 使用与实际方块相同的精确旋转中心(取第二和第三方块中间)
let (center_x, center_y) = match tetromino {
Tetromino::I => (1, 2), // I型中心(第二和第三方块中间)
Tetromino::O => (1, 1), // O型中心(四个方块中间)
Tetromino::J => (1, 1), // J型中心(第二和第三方块中间)
Tetromino::L => (1, 1), // L型中心(第二和第三方块中间)
Tetromino::S => (1, 1), // S型中心(第二和第三方块中间)
Tetromino::Z => (1, 1), // Z型中心(第二和第三方块中间)
Tetromino::T => (1, 1), // T型中心(第二个方块)
};
// 根据旋转状态计算相对位置
let positions = match (tetromino, rotation_mod) {
(Tetromino::I, 0) => vec![(center_x, center_y-2), (center_x, center_y-1), (center_x, center_y), (center_x, center_y+1)],
(Tetromino::I, 1) => vec![(center_x-1, center_y), (center_x, center_y), (center_x+1, center_y), (center_x+2, center_y)],
(Tetromino::I, 2) => vec![(center_x, center_y-2), (center_x, center_y-1), (center_x, center_y), (center_x, center_y+1)],
(Tetromino::I, 3) => vec![(center_x-1, center_y), (center_x, center_y), (center_x+1, center_y), (center_x+2, center_y)],
(Tetromino::O, _) => vec![(center_x, center_y), (center_x, center_y+1), (center_x+1, center_y), (center_x+1, center_y+1)],
// J型方块定义
(Tetromino::J, 0) => vec![(center_x, center_y), (center_x+1, center_y), (center_x+1, center_y+1), (center_x+1, center_y+2)],
(Tetromino::J, 1) => vec![(center_x, center_y+1), (center_x, center_y+2), (center_x+1, center_y+1), (center_x+2, center_y+1)],
(Tetromino::J, 2) => vec![(center_x+1, center_y), (center_x+1, center_y+1), (center_x+1, center_y+2), (center_x+2, center_y+2)],
(Tetromino::J, 3) => vec![(center_x, center_y+1), (center_x+1, center_y+1), (center_x+2, center_y), (center_x+2, center_y+1)],
// L型方块定义
(Tetromino::L, 0) => vec![(center_x, center_y+2), (center_x+1, center_y), (center_x+1, center_y+1), (center_x+1, center_y+2)],
(Tetromino::L, 1) => vec![(center_x, center_y+1), (center_x+1, center_y+1), (center_x+2, center_y+1), (center_x+2, center_y+2)],
(Tetromino::L, 2) => vec![(center_x+1, center_y), (center_x+1, center_y+1), (center_x+1, center_y+2), (center_x+2, center_y)],
(Tetromino::L, 3) => vec![(center_x, center_y), (center_x, center_y+1), (center_x+1, center_y+1), (center_x+2, center_y+1)],
(Tetromino::S, 0) => vec![(center_x, center_y), (center_x, center_y+1), (center_x+1, center_y-1), (center_x+1, center_y)],
(Tetromino::S, 1) => vec![(center_x-1, center_y), (center_x, center_y), (center_x, center_y+1), (center_x+1, center_y+1)],
(Tetromino::S, 2) => vec![(center_x-1, center_y), (center_x-1, center_y+1), (center_x, center_y-1), (center_x, center_y)],
(Tetromino::S, 3) => vec![(center_x-1, center_y-1), (center_x, center_y-1), (center_x, center_y), (center_x+1, center_y)],
(Tetromino::T, 0) => vec![(center_x, center_y), (center_x+1, center_y-1), (center_x+1, center_y), (center_x+1, center_y+1)],
(Tetromino::T, 1) => vec![(center_x-1, center_y), (center_x, center_y), (center_x, center_y+1), (center_x+1, center_y)],
(Tetromino::T, 2) => vec![(center_x, center_y-1), (center_x, center_y), (center_x, center_y+1), (center_x-1, center_y)],
(Tetromino::T, 3) => vec![(center_x-1, center_y), (center_x, center_y-1), (center_x, center_y), (center_x+1, center_y)],
// Z型方块定义
(Tetromino::Z, 0) => vec![(center_x, center_y), (center_x, center_y+1), (center_x+1, center_y+1), (center_x+1, center_y+2)],
(Tetromino::Z, 1) => vec![(center_x, center_y+1), (center_x+1, center_y), (center_x+1, center_y+1), (center_x+2, center_y)],
(Tetromino::Z, 2) => vec![(center_x, center_y), (center_x, center_y+1), (center_x+1, center_y+1), (center_x+1, center_y+2)],
(Tetromino::Z, 3) => vec![(center_x, center_y+1), (center_x+1, center_y), (center_x+1, center_y+1), (center_x+2, center_y)],
_ => unreachable!("Invalid rotation state"),
};
//log!("[PREVIEW] Calculated relative positions: {:?}", positions);
view! {
<div style="display: grid; grid-template-columns: repeat(4, 20px); grid-template-rows: repeat(4, 20px); width: 80px; height: 80px; gap: 1px;">
{positions.into_iter().map(|(i, j)| {
view! {
<div
style=format!("grid-row: {}; grid-column: {};", i + 1, j + 1)
class=format!("{} tetromino-border", tile_color(Some(tetromino)))
></div>
}
}).collect::<Vec<_>>()}
</div>
}
}
// 重置游戏函数
let reset = move |_| {
set_game.update(|g| *g = Game::new());
};
// 暂停/继续游戏函数
let toggle_pause = move |_| {
set_game.update(|g| g.toggle_pause());
};
// 游戏界面视图
view! {
<div class="game-container">
<div class="game-header">
//<h1 class="game-title" style="display: block; text-align: center; width: 100%; margin-bottom: 1rem;">"俄罗斯方块"</h1>
<div class="game-controls-container">
<div class="game-scores-container">
<div class="game-score-box">
<div class="game-score-label">"得分:"{move || game.get().score}</div>
</div>
<div class="game-score-box">
<div class="game-score-label">"等级:"{move || game.get().level}</div>
</div>
</div>
<div class="game-buttons-container">
<button
on:click=toggle_pause
class="game-button"
>
{move || if game.get().paused { "继续" } else { "暂停" }}
</button>
<button
on:click=reset
class="game-button"
>
"新游戏"
</button>
</div>
</div>
</div>
<div style="display: flex; justify-content: space-between; align-items: flex-start; width: 100%; margin-top: 1rem;">
<div class="game-main-area">
<div class="game-board">
<div style="display: grid; grid-template-columns: repeat(10, 24px); grid-template-rows: repeat(20, 24px); gap: 1px;">
{move || {
// 创建网格副本用于渲染
let mut render_grid = game.get().grid.clone();
// 添加当前方块到渲染网格
if !game.get().game_over {
for &[i, j] in &game.get().current_piece.1 {
if i >= 0 {
render_grid[i as usize][j as usize] = Some(game.get().current_piece.0);
}
}
}
// 渲染网格
render_grid.iter().flat_map(|row| {
row.iter().map(|&tile| {
view! {
<div class=format!("{} tetromino-border", tile_color(tile))></div>
}
})
}).collect::<Vec<_>>()
}}
</div>
</div>
</div>
<div class="game-side-panel">
<div class="game-preview">
<h2 class="game-preview-title">"下一个"</h2>
{move || {
let (tetromino, rotation) = game.get().next_piece;
render_preview((tetromino, rotation))
}}
</div>
<div class="game-instructions">
<h2 class="game-instructions-title">"操作说明"</h2>
<ul class="game-instructions-list">
<li>"← → : 左右移动"</li>
<li>"↓ : 加速下落"</li>
<li>"↑ : 旋转方块"</li>
<li>"空格 : 硬降到底部"</li>
<li>"P : 暂停/继续"</li>
</ul>
</div>
</div>
</div>
{/* 游戏结束提示 */}
<Show when=move || game.get().game_over>
<div class="game-over-message">
"游戏结束! 最终得分: " {move || game.get().score}
</div>
</Show>
{/* 暂停提示 */}
<Show when=move || game.get().paused>
<div class="game-paused-message">
"游戏暂停"
</div>
</Show>
</div>
}
}
DeepSeek刚写出来的程序bug比较多,要一步一步引导其修改完善。游戏界面的css设置文件内容如下:
/* 俄罗斯方块颜色 */
.tetromino-i {
background-color: #06b6d4; /* I方块 - 青色 */
}
.tetromino-l {
background-color: #f97316; /* L方块 - 橙色 */
}
.tetromino-z {
background-color: #ef4444; /* Z方块 - 红色 */
}
.tetromino-j {
background-color: #2563eb; /* J方块 - 深蓝色 */
}
.tetromino-s {
background-color: #10b981; /* S方块 - 绿色 */
}
.tetromino-o {
background-color: #facc15; /* O方块 - 黄色 */
}
.tetromino-t {
background-color: #a855f7; /* T方块 - 紫色 */
}
.tetromino-empty {
background-color: #1f2937; /* 空单元格 - 深灰色 */
}
/* 边框样式 */
.tetromino-border {
border: 1px solid #374151;
}
/* 按钮样式 */
.game-button {
background-color: #3b82f6;
color: #000;
padding: 0.5rem 1rem;
border-radius: 0.25rem;
margin: 10px 5px;
}
.game-button:hover {
background-color: #2563eb;
}
/* 容器样式 */
.game-container {
width: 550px;
margin-left: auto;
margin-right: auto;
padding: 1rem;
}
.game-header {
display: block;
margin-bottom: 1rem;
}
.game-title {
font-size: 1.875rem;
font-weight: bold;
text-align: center;
margin-bottom: 1rem;
display: block;
}
.game-score-container {
display: flex;
justify-content: center;
gap: 1rem;
margin-bottom: 1rem;
}
.game-title {
font-size: 1.875rem;
font-weight: bold;
}
.game-controls-container {
display: flex;
justify-content: center;
align-items: center;
gap: 2rem;
width: 100%;
}
.game-scores-container {
display: flex;
gap: 1rem;
align-items: center;
justify-content: center;
}
.game-buttons-container {
display: flex;
gap: 1rem;
align-items: center;
justify-content: center;
}
.game-score-box {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
}
.game-score-container {
display: flex;
gap: 1rem;
}
.game-score-box {
background-color: #e5e7eb;
padding: 0.5rem;
border-radius: 0.25rem;
}
.game-score-label {
font-size: 1rem;
font-weight: bold;
}
.game-score-value {
font-size: 1.25rem;
font-weight: bold;
}
.game-content-wrapper {
display: flex;
gap: 1rem;
}
.game-main-area {
width:250px;
margin:0px 0px 0px 10px;
}
.game-board {
width:100%;
background-color: #1f2937;
padding: 0.5rem;
border-radius: 0.5rem;
}
.game-side-panel {
width:250px;
}
.game-preview {
width:100%;
background-color: #e5e7eb;
padding: 1rem;
border-radius: 0.25rem;
}
.game-instructions {
width:100%;
background-color: #e5e7eb;
padding: 1rem;
border-radius: 0.25rem;
}
.game-over-message {
margin-top: 1rem;
padding: 1rem;
background-color: #ef4444;
color: #000;
border-radius: 0.25rem;
text-align: center;
}
.game-paused-message {
margin-top: 1rem;
padding: 1rem;
background-color: #facc15;
color: #000;
border-radius: 0.25rem;
text-align: center;
}
总体而言,DeekSeek辅助编程效率还是很高的,对于编程初学者尤其方便。