PureScript 语言 实现 2048 游戏 数字合并 + 动画效果 + 本地排名

PureScript阿木 发布于 2025-06-11 17 次阅读


PureScript 2048 游戏实现:数字合并、动画效果与本地排名

2048 是一款简单的数字合并游戏,玩家通过滑动屏幕来合并相邻的数字,目标是创建一个 2048 的方块。PureScript 是一种函数式编程语言,以其简洁和高效著称。我们将使用 PureScript 来实现一个具有数字合并、动画效果和本地排名的 2048 游戏。

技术栈

- PureScript
- Purescript-react
- Purescript-eff
- Purescript-arrays
- Purescript-dom

游戏设计

2048 游戏的核心是一个 4x4 的网格,每个格子可以放置一个数字(2 或 4)。玩家可以通过上、下、左、右四个方向滑动来移动数字。当两个相同的数字相遇时,它们会合并成一个更大的数字。游戏的目标是创建一个 2048 的方块。

实现步骤

1. 初始化游戏状态

我们需要定义游戏的状态,包括网格、分数和最高分。

purescript
import Data.Array (Array, replicate, (!!), zipWith, map, foldl, foldr, (:))
import Data.Int (toNumber)
import Data.Maybe (Maybe, fromJust)
import Data.Tuple (Tuple, fst, snd)

type Grid = Array (Array Int)
type State = { grid :: Grid, score :: Int, highScore :: Int }

initialState :: State
initialState = { grid: replicate 4 (replicate 4 0), score: 0, highScore: 0 }

2. 生成随机数字

在游戏开始时,我们需要在网格中随机放置两个数字(2 或 4)。

purescript
randomInt :: Int -> Int -> Int
randomInt min max = min + (toNumber (floor (random () (max - min + 1)))) :: Int

randomNumber :: Int -> Int
randomNumber size = randomInt 2 4

generateRandomNumber :: Grid -> State -> State
generateRandomNumber grid state =
let
emptyPositions = [ (i, j) | i <- [0..3], j <- [0..3], grid !! i !! j == 0 ]
randomPosition = emptyPositions !! (randomInt 0 (length emptyPositions - 1))
(i, j) = randomPosition
in
{ grid: grid !! i !~ grid !! i !! j + randomNumber size, ...state }

3. 移动数字

玩家可以通过滑动屏幕来移动数字。我们需要实现一个函数来处理这种移动。

purescript
moveRight :: Grid -> Grid
moveRight grid = foldr (row acc -> foldr ( acc' -> [n] acc') [0] row acc) grid

moveLeft :: Grid -> Grid
moveLeft grid = foldr (row acc -> foldr ( acc' -> acc' [n]) [0] row acc) grid

moveUp :: Grid -> Grid
moveUp grid = foldr (row acc -> foldr ( acc' -> acc' [n]) [0] row acc) grid

moveDown :: Grid -> Grid -> Grid
moveDown grid = foldr (row acc -> foldr ( acc' -> acc' [n]) [0] row acc) grid

4. 合并数字

当两个相同的数字相遇时,它们会合并成一个更大的数字。

purescript
merge :: Array Int -> Array Int
merge = foldr ( acc -> if acc == [] || n /= acc !! (length acc - 1) then n : acc else acc) []

mergeRow :: Array Int -> Array Int
mergeRow row = merge row

mergeGrid :: Grid -> Grid
mergeGrid grid = zipWith mergeRow grid

5. 检查游戏结束

当没有更多的数字可以合并时,游戏结束。

purescript
gameOver :: Grid -> Boolean
gameOver grid = not (foldr (row acc -> not (foldr ( acc' -> n /= 0 && acc') false row) acc) grid)

6. 游戏逻辑

现在我们可以将所有这些函数组合起来,创建一个游戏循环。

purescript
gameLoop :: State -> State
gameLoop state =
let
newGrid = mergeGrid state.grid
scoreChange = foldl (acc row -> acc + (foldl (acc' n -> if n /= 0 then acc' + 1 else acc') 0 row)) 0 newGrid
newScore = state.score + scoreChange
newHighScore = max newScore state.highScore
in
if gameOver newGrid then
{ grid: newGrid, score: newScore, highScore: newHighScore }
else
generateRandomNumber newGrid { state | score: newScore, highScore: newHighScore }

7. 渲染游戏

使用 Purescript-react 和 Purescript-eff 来渲染游戏界面。

```purescript
import React (createElement, ReactClass, ReactElement, ReactType, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType_, ReactType