腾讯面试题:构建命令行下的石头剪刀布游戏

380 阅读6分钟

前言

  • 基于冯诺依曼原理, 编写石头剪刀布的游戏

在这篇文章中我们将介绍使用冯诺依曼原理来编写石头剪刀布游戏

冯诺依曼原理中的 计算机组成:冯·诺依曼架构认为计算机应由五大基本部件组成,包括:

-   **运算器**(Arithmetic Unit):负责执行算术和逻辑运算。
-   **控制器**(Control Unit):负责协调各部件工作,解读并执行存储在内存中的指令。
-   **存储器**(Memory):用于存储程序和数据,程序指令和数据以相同的二进制形式存储。
-   **输入设备**(Input Devices):用于向计算机输入数据和指令。
-   **输出设备**(Output Devices):用于展示计算机处理结果。

我们要想完成这个游戏则需要这几个步骤:

输入

玩家输入他们的选择(石头、剪刀或布),并从计算机生成一个随机选择。

处理

程序对输入进行验证,比较玩家和计算机的选择,确定游戏结果,即如何判定胜负呢?

输出

程序输出结果,告诉玩家他们是赢了、输了还是平局。

那么我们该如何完成输入与输出.......

正文

这个代码片段是在Node.js中使用process.stdin来读取标准输入的数据:

process.stdin.on('data', (buffer) =>{
    const action = buffer.toString();
    console.log(action,'------');
})   

这段代码是使用Node.js编写的,它用于处理标准输入(stdin)的事件。在Node.js环境中,process是一个全局对象,提供了与当前Node.js进程相关的各种属性和方法,其中包括对标准输入、输出的支持的作用是持续监听控制台的输入,一旦有输入内容,就将其打印出来,常用于构建需要接收用户命令行输入的简单交互式程序。

  • 在理解输入部分后我们开始看看输入部分的代码
const game = (action) => {
    const arr = ['rock', 'scissor', 'paper'];
    console.log(action);
    // 输入校验
    if (arr.indexOf(action) == -1) {
        throw new Error('用户输入错误');
    }

这段代码定义了一个名为game的函数,它接受一个参数action,用于模拟实现石头、剪刀、布游戏中对用户输入的处理逻辑。

  1. 函数定义: const game = (action) => { ... } 定义了一个箭头函数,名为game,它接收一个参数action,这个参数代表玩家的选择(石头、剪刀或布)。

  2. 定义选项数组: const arr = ['rock', 'scissor', 'paper']; 创建了一个数组arr,其中包含了游戏的所有有效选项——石头(rock)、剪刀(scissor)和布(paper)。

  3. 输入校验:

    • if (arr.indexOf(action) == -1) { ... } 通过检查action在数组arr中的索引来判断玩家的输入是否有效。
    • arr.indexOf(action) 返回action在数组中的索引,如果不存在则返回-1。
    • 如果action不在arr中(即indexOf结果为-1),则执行throw new Error('用户输入错误'); 抛出一个错误,错误信息是'用户输入错误',意味着玩家输入了无效的选项,比如"lizard"或"spock",或者拼写错误等。

    然后我们就可以在控制台看见我们输入的'rock', 'scissor', 'paper'都能被打印出来。那么我们就要进行到下一步,如何让电脑来判定胜负呢?

 let computerAction;
    let random = Math.floor(Math.random() * 3)
    computerAction = arr[random];
    // console.log('电脑出了' + computerAction);
    if (computerAction == action) {
        console.log('平局')
        return 0; // 平局
    } else if (
        (computerAction == 'rock' && action == 'scissor') ||
        (computerAction == 'scissor' && action == 'paper') ||
        (computerAction == 'paper' && action == 'rock')
    ) {
        console.log('你输了')
        return -1;
    } else {
        console.log('你赢了');
        return 1;
    }

}
    • 初始化电脑选择: let computerAction; 声明一个变量用于存储电脑的选择。

    • 随机选择:

      • let random = Math.floor(Math.random() * 3); 这行代码生成一个0到2之间的随机整数,用于从arr数组中随机选择一个动作。Math.random()生成0到1之间的随机数,乘以3后范围变为0到3之间,但不包括3。Math.floor()函数则向下取整,确保结果为0、1或2。
      • computerAction = arr[random]; 根据随机数从数组arr中选择电脑的动作。
    • 判断胜负逻辑:

      • 平局: 如果computerActionaction相等,即玩家和电脑出的一样,输出'平局',函数返回0。
      • 玩家输: 通过一系列条件判断(例如:电脑出石头玩家出剪刀,电脑出剪刀玩家出布,电脑出布玩家出石头),如果玩家输,输出'你输了',函数返回-1。
  • 在完成判定胜负代码后,我们如何对胜利次数进行记录并累计,最后通过3局两胜来判定输赢呢?
let winCount = 0;

process.stdin.on('data', (buffer) => {
    // 存储和通信的底层是二进制 
    // console.log(buffer);
    const action = buffer.toString().trim();
    // console.log(action, '------');
    // 独立的随机出拳业务
    const result = game(action);
    if(result == 1){
        winCount ++
        if(winCount == 3){
            console.log('不玩了');
            process.exit();
        }
    } 
})
  1. 初始化胜利计数器: let winCount = 0; 初始化一个变量用于记录玩家获胜的次数。

  2. 监听标准输入: process.stdin.on('data', ...) 监听来自标准输入(通常是键盘输入)的数据事件。当用户在命令行输入内容并按下回车键时,这段代码会执行。

  3. 处理用户输入:

    • const action = buffer.toString().trim(); 将接收到的二进制数据转换为字符串,并去除首尾空白字符。这里假设用户输入的是游戏中的一个合法动作:"rock"、"paper"或"scissor"。
    • 紧接着调用game(action)函数来处理这一轮的游戏逻辑,这个函数应该就是您之前提供的那段代码所定义的,它接收用户的选择作为参数,然后根据游戏规则返回游戏结果(-1表示输,0表示平局,1表示赢)。
  4. 根据游戏结果更新胜利计数并判断是否结束游戏:

    • 如果result == 1,说明玩家赢得了这一轮,于是增加winCount的值。
    • winCount达到3时,输出'不玩了',并调用process.exit();结束当前进程,也就是结束了整个游戏。

这个程序构建了一个简单的交互式"石头、剪刀、布"游戏,用户通过命令行输入他们的选择,程序会根据游戏规则判断结果,并在用户连续赢得三场后自动结束游戏。

结语

通过本篇文章的逐步解析与实践,我们不仅重温了冯·诺依曼架构这一计算机科学基石的重要性,还亲手构建了一个趣味横生的石头、剪刀、布游戏。在这个过程中,从玩家输入的捕获、游戏逻辑的精密判断,到胜利条件的追踪与游戏终止的自动化处理,每一步都直观映射了计算机系统的基本组成部分及其协作方式。

更重要的是,这个简短的编程之旅揭示了技术背后的人文情怀——即使是最基本的架构原则,也能激发无限创意,带来欢乐与思考。