Wordless: 一个周末打造的小爆游戏

这个项目是什么

Wordless就是个类似 Wordle 的猜单词游戏,用 Next.js 搭建的。玩家有 6 次机会猜出单词,支持 3 到 8 个字母的单词。说实话,开始只是想做点跟 wordle 不一样东西,没想到做着做着就越来越有意思了。

点击这里可以体验:​https://wordless.online/​

我用了一个周末把代码撸完以后,直接发布上线,也没怎么关注,没想到几个月过去了,这个小游戏的流量一直很稳定,有 50%的自然搜索,40%的直接访问流量,这可是个非常漂亮的流量数据呀。

用了什么技术

主要框架

  • React 18.3.1 - 没什么好说的,前端必备

  • Next.js 14.2.4 - 选它主要是因为 SSR 和 API 路由很方便

  • TypeScript - 虽然写起来麻烦点,但能避免很多低级错误

  • Tailwind CSS - 写样式贼快,不用想类名

UI 相关

  • Radix UI - 弹窗、通知这些组件用的,无障碍做得不错

  • Lucide React - 图标库,简洁好看

  • Canvas Confetti - 猜对了撒彩带的特效,挺有意思的

其他工具

  • SWR - 数据获取用的,缓存机制不错

  • Zod - 数据验证,比手写 if-else 强多了

  • nspell - 检查单词拼写是否正确

文件结构

wordlessgame/
├── src/
│   ├── app/                    # Next.js App Router
│   │   ├── api/               # API 路由
│   │   │   ├── ai-completion/ # AI 辅助功能
│   │   │   ├── validate-word/ # 单词验证
│   │   │   └── words/        # 单词生成
│   │   ├── layout.tsx        # 根布局
│   │   └── page.tsx         # 主页面
│   ├── components/           # React 组件
│   │   ├── ui/              # 基础 UI 组件
│   │   ├── game-grid.tsx    # 游戏网格
│   │   ├── key-board.tsx    # 虚拟键盘
│   │   └── result-modal.tsx # 结果弹窗
│   ├── data/                # 静态数据
│   │   └── word-lists.ts    # 单词词库
│   ├── lib/                 # 工具函数
│   │   ├── api.ts          # API 调用
│   │   └── utils.ts        # 工具函数
│   ├── ai/                 # AI 相关功能
│   └── styles/             # 样式文件
└── public/                 # 静态资源

核心功能实现

1. 游戏状态管理

游戏使用 React Hooks 管理复杂的状态:

// 主要状态
const [columns, setColumns] = useState(3);           // 单词长度
const [gridContent, setGridContent] = useState<string[]>([]); // 网格内容
const [currentCell, setCurrentCell] = useState(-1);  // 当前输入位置
const [word, setWord] = useState('');                // 目标单词
const [matchResults, setMatchResults] = useState<string[]>([]); // 匹配结果
const [cellMatchClasses, setCellMatchClasses] = useState<string[]>([]); // 样式类

2. 单词生成系统

采用优化的单词生成器,避免重复选择同一单词:

class WordGenerator {
  private static instance: WordGenerator;
  private readonly cache: Map<number, string[]> = new Map();
  private readonly usedIndices: Map<number, Set<number>> = new Map();
  private readonly shuffledIndices: Map<number, number[]> = new Map();

  public getRandomWord(length: number): string {
    // Fisher-Yates 洗牌算法确保随机性
    // 避免重复选择相同单词
  }
}

3. 游戏逻辑算法

const matchWord = (guessedWord: string, targetWord: string) => {
  const result = new Array(guessedWord.length).fill('X'); // X=不匹配
  const targetCounts = new Map<string, number>();
  
  // 第一遍:标记完全匹配的字母
  for (let i = 0; i < guessedWord.length; i++) {
    if (guessedWord[i] === targetWord[i]) {
      result[i] = 'C'; // C=正确位置
    } else {
      targetCounts.set(targetWord[i]!, (targetCounts.get(targetWord[i]!) || 0) + 1);
    }
  }
  
  // 第二遍:标记位置错误的字母
  for (let i = 0; i < guessedWord.length; i++) {
    if (result[i] !== 'C') {
      const char = guessedWord[i]!;
      if (targetCounts.get(char)! > 0) {
        result[i] = 'P'; // P=位置错误
        targetCounts.set(char, targetCounts.get(char)! - 1);
      }
    }
  }
  
  return result;
};

4. 游戏网格组件

export function GameGrid({
  gridContent,
  columns,
  currentCell,
  cellMatchClasses,
  flippingRows
}: GameGridProps) {
  return (
    <div className={`grid ${gridCol} gap-2 mb-8`}>
      {gridContent.map((content, index) => {
        const matchClass = cellMatchClasses[index];
        const isFlipping = flippingRows.has(Math.floor(index / columns));
        
        return (
          <div 
            className={cn(
              'w-14 h-14 flex items-center justify-center text-2xl font-bold rounded-md',
              matchClass === 'C' ? 'bg-green-500 text-white' : 
              matchClass === 'P' ? 'bg-yellow-500 text-white' : 
              matchClass === 'X' ? 'bg-zinc-400 text-white' : 'bg-white',
              isFlipping ? 'animate-flip' : ''
            )}
            style={{
              animationDelay: isFlipping ? `${(index % columns) * 100}ms` : '0ms'
            }}
          >
            {content}
          </div>
        );
      })}
    </div>
  );
}

5. 虚拟键盘组件

使用 memo 优化渲染性能:

const KeyButton = memo(({ 
  letter, 
  isMatched, 
  noMatched, 
  onClick 
}: KeyButtonProps) => {
  return (
    <button 
      onClick={() => onClick(letter)} 
      className={cn(
        'w-14 h-14 rounded-md font-bold transition-colors',
        isMatched ? 'bg-green-500 text-white' : 
        noMatched ? 'bg-zinc-400 text-white' : 
        'bg-white hover:bg-violet-50'
      )}
    >
      {letter}
    </button>
  );
});

数据结构

单词词库

词库按长度分类存储,每个长度包含 500+ 个单词:

export const WORD_LISTS: Record<number, string[]> = {
  3: ['ace', 'age', 'air', ...], // 500+ 三字母单词
  4: ['able', 'acid', 'aged', ...], // 500+ 四字母单词
  5: ['about', 'above', 'abuse', ...], // 500+ 五字母单词
  6: ['abroad', 'accept', 'access', ...], // 500+ 六字母单词
  7: ['abandon', 'ability', 'absence', ...], // 500+ 七字母单词
  8: ['absolute', 'academic', 'accepted', ...] // 500+ 八字母单词
};

用户体验优化

1. 动画效果

  • 卡片翻转动画:使用 CSS animate-flip

  • 按键反馈:按键按下时的视觉反馈

  • 胜利庆祝:使用 canvas-confetti 库

2. 响应式设计

  • 移动端优化的键盘布局

  • 自适应网格大小

  • 触摸友好的交互

3. 性能优化

  • 使用 React.memo 减少不必要的重渲染

  • useCallback 和 useMemo 优化函数和计算

  • 单词生成器的缓存机制

部署和 SEO

SEO 优化

 

export const metadata: Metadata = { title: "Unlimited Wordless Online: Guess the Word in 6 Tries!", description: "Wordless Online: Endless Word Challenges...", keywords: "wordless,wordly, wordle, game, puzzle, word, words, letters, play, online, guess,unlimited", };

分析工具

  • Google Analytics 用户行为追踪

  • 自定义事件追踪

开发工具

代码质量

  • ESLint - 代码规范检查

  • Prettier - 代码格式化

  • TypeScript - 类型安全

包管理

  • PNPM - 快速、节省磁盘空间的包管理器

游戏流程

  1. 初始化:选择单词长度(3-8个字母)

  2. 生成目标单词:从词库中随机选择

  3. 用户输入:通过虚拟键盘或物理键盘输入

  4. 验证输入:检查是否为有效单词

  5. 匹配算法:计算字母匹配情况

  6. 视觉反馈:显示颜色提示(绿色=正确位置,黄色=错误位置,灰色=不存在)

  7. 游戏结束:6次尝试后或猜中单词后结束

  8. 结果展示:显示结果弹窗和庆祝动画

特色功能

1. 可变单词长度

支持 3-8 个字母的单词,增加游戏难度选择

2. 智能单词生成

避免重复,确保每次游戏都有新鲜感

3. 实时反馈

即时的视觉和交互反馈,提升用户体验

4. 无限游戏

没有次数限制,可以连续游戏

5. 响应式设计

适配各种设备屏幕尺寸

技术亮点

  1. 现代化技术栈:使用最新的 React、Next.js 和 TypeScript

  2. 性能优化:多层次的性能优化策略

  3. 可维护性:清晰的代码结构和组件分离

  4. 用户体验:流畅的动画和交互效果

  5. 可扩展性:模块化设计便于功能扩展

总结

Wordless 是一款用现代 Web 技术打造的游戏,干净利落,跑得飞快,代码还容易维护。没堆花里胡哨的东西,但该有的全都有——好玩、流畅、写得明白。

欢迎来体验: https://wordless.online/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值