井字棋规则简单,却是练习"棋盘建模"和"AI 决策"的绝佳素材。你和电脑轮流落子,电脑带策略 AI——能赢就赢,赢不了就堵,堵不住占中心。只用标准库。
玩法介绍
3×3 棋盘,你执 X,AI 执 O。输入行号列号(1~3)落子,横竖斜连成三个即胜,下满则平局。例如 2 2 是正中间。
第 1 步:用 vector 表示棋盘
用 std::vector<std::vector<char>> 表示棋盘:外层 3 行,内层 3 字符。空位用空格 ' ',绘制时直接输出即可:
#include <iostream>
#include <vector>
using Board = std::vector<std::vector<char>>;
Board makeBoard() {
return Board(3, std::vector<char>(3, ' '));
}
第 2 步:绘制棋盘
逐行输出,行间用分隔线隔开。传 const Board& 引用避免复制,也防止函数内意外修改:
void draw(const Board& b) {
for (int r = 0; r < 3; ++r) {
std::cout << " " << b[r][0] << " | " << b[r][1] << " | " << b[r][2] << "\n";
if (r < 2) std::cout << "---+---+---\n";
}
}
第 3 步:落子与胜负判定
胜负判定是核心:遍历 3 行、3 列、两条对角线,看是否被同一棋子占满;full() 检测平局:
bool win(const Board& b, char p) {
for (int i = 0; i < 3; ++i) {
if (b[i][0] == p && b[i][1] == p && b[i][2] == p) return true;
if (b[0][i] == p && b[1][i] == p && b[2][i] == p) return true;
}
return (b[0][0] == p && b[1][1] == p && b[2][2] == p) ||
(b[0][2] == p && b[1][1] == p && b[2][0] == p);
}
bool full(const Board& b) {
for (const auto& row : b)
for (char c : row)
if (c == ' ') return false;
return true;
}
第 4 步:AI 决策
AI 的策略是"先想一步":① 遍历空位,试着落 O,能赢就落;② 再假装落 X,玩家会赢就堵住;③ 都不行就占中心,最后找第一个空位。这种"试了再撤销"的技巧叫回溯试探,是 Minimax 算法的雏形:
bool moveAI(Board& b) {
// 1 能赢就赢
for (int r = 0; r < 3; ++r)
for (int c = 0; c < 3; ++c)
if (b[r][c] == ' ') {
b[r][c] = 'O';
if (win(b, 'O')) return true;
b[r][c] = ' '; // 撤销试探
}
// 2 玩家要赢就堵
for (int r = 0; r < 3; ++r)
for (int c = 0; c < 3; ++c)
if (b[r][c] == ' ') {
b[r][c] = 'X';
if (win(b, 'X')) { b[r][c] = 'O'; return true; }
b[r][c] = ' ';
}
// 3 占中心,否则找空位
if (b[1][1] == ' ') { b[1][1] = 'O'; return true; }
for (int r = 0; r < 3; ++r)
for (int c = 0; c < 3; ++c)
if (b[r][c] == ' ') { b[r][c] = 'O'; return true; }
return false; // 棋盘满了
}
第 5 步:主循环与编译运行
最后写 main():落子前校验位置合法性,落子后依次检查"玩家赢 / 平局 / AI 落子 / AI 赢 / 平局"。把第 1~4 步的函数与下面的 main() 存进同一个 tictactoe.cpp:
int main() {
Board b = makeBoard();
std::cout << "井字棋!你执 X,AI 执 O。输入行列(1~3),如 2 2\n";
while (true) {
draw(b);
int r, c;
std::cout << "你的回合(行 列): ";
std::cin >> r >> c;
--r; --c;
if (r < 0 || r > 2 || c < 0 || c > 2 || b[r][c] != ' ') {
std::cout << "无效位置,重新输入!\n";
continue;
}
b[r][c] = 'X';
if (win(b, 'X')) { draw(b); std::cout << "你赢了!\n"; break; }
if (full(b)) { draw(b); std::cout << "平局。\n"; break; }
moveAI(b);
if (win(b, 'O')) { draw(b); std::cout << "AI 赢了!\n"; break; }
if (full(b)) { draw(b); std::cout << "平局。\n"; break; }
}
return 0;
}
g++ -std=c++17 -o tictactoe tictactoe.cpp
./tictactoe
这个 AI 赢不了你时一定会堵住你。把策略 3 改成"随机落子",棋路更有变化。
💡 改进方向:① 用 Minimax 实现"不输"的完美 AI;② 支持先手/后手;③ 加计分板;④ 改成 N×N 连四子。