You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C语言四子棋中实现30秒走步倒计时及5秒提醒功能?

四子棋30秒走步限制+倒计时提醒实现方案

核心思路

利用信号机制实现定时提醒与超时判定,结合IO多路复用解决scanf()阻塞导致的定时器失效问题。


具体实现步骤

  1. 信号与定时器配置

    • 注册SIGALRM信号处理函数,用于响应定时事件。
    • 放弃单一的alarm(30),分阶段触发:先设置25秒定时器触发“剩余5秒”提醒,再设置5秒定时器触发超时判定。
    • 用volatile修饰全局标志(如warn_triggered、timeout_triggered),确保信号处理函数的修改能被主循环实时感知。
  2. 解决输入阻塞问题

    • 不能直接用阻塞式的scanf(),否则定时器触发时无法中断输入流程。推荐用select()实现带超时的输入监听:
      • 每次循环中用select()等待标准输入可读,同时设置剩余时间为超时阈值。
      • 若select()返回0,说明超时;返回大于0则读取输入并校验有效性。
  3. 信号处理函数的极简设计

    • 信号处理函数内只做基础操作:修改全局标志、打印提示,避免调用非重入函数(如复杂IO操作),保证信号处理的安全性。

核心代码片段示例

#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/select.h>

// 全局标志,用volatile确保跨信号的可见性
volatile int warn_triggered = 0;
volatile int timeout_triggered = 0;

void sigalrm_handler(int sig) {
    if (!warn_triggered) {
        printf("\n⚠️ 剩余5秒,请尽快走步!\n");
        warn_triggered = 1;
        alarm(5); // 触发最后5秒的超时定时器
    } else {
        printf("\n⏰ 时间耗尽!\n");
        timeout_triggered = 1;
    }
}

int main() {
    signal(SIGALRM, sigalrm_handler);
    int move;

    while(1) {
        warn_triggered = 0;
        timeout_triggered = 0;
        alarm(25); // 25秒后触发提醒

        printMat();

        do {
            // 检查超时标志,直接终止输入循环
            if (timeout_triggered) break;

            printf("\nMake a move: ");
            fflush(stdout); // 确保提示语及时输出

            fd_set read_fds;
            struct timeval tv;
            FD_ZERO(&read_fds);
            FD_SET(STDIN_FILENO, &read_fds);

            // 动态设置剩余超时时间
            tv.tv_sec = warn_triggered ? 5 : 25;
            tv.tv_usec = 0;

            int select_ret = select(STDIN_FILENO + 1, &read_fds, NULL, NULL, &tv);
            if (select_ret == 0) {
                // select超时,触发超时标志
                timeout_triggered = 1;
                break;
            } else if (select_ret > 0) {
                // 读取输入并校验有效性
                scanf("%d", &move);
                if (!isNotValid(move)) break;
                printf("无效走步,请重新输入!\n");
            }
        } while(1);

        alarm(0); // 取消未触发的定时器

        // 处理超时逻辑
        if (timeout_triggered) {
            printf("当前玩家超时,判负!\n");
            SwapPlayer();
            continue;
        }

        // 正常走步逻辑
        move(move, player_symbol);
        SwapPlayer();
    }
    return 0;
}

关键注意事项

  • select()的超时时间需要根据是否触发提醒动态调整,避免时间计算偏差。
  • 信号处理函数中尽量只调用printf这类简单可重入函数,减少潜在风险。
  • 每次回合开始前必须重置全局标志和定时器,避免上一回合的状态干扰当前回合。

内容的提问来源于stack exchange,提问作者Enange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 02:44:59