Tauri(2.5.1)+Leptos(0.8.2)开发自用桌面小程序--DeepSeek辅助编程(俄罗斯方块)

发布于:2025-06-26 ⋅ 阅读:(14) ⋅ 点赞:(0)

 在之前工作基础上(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辅助编程效率还是很高的,对于编程初学者尤其方便。


网站公告

今日签到

点亮在社区的每一天
去签到