如何将C语言编写的黑白棋(Reversi)Bot算法接入Web应用?
实现Web GUI调用C语言黑白棋Bot的方案
方案一:将C Bot封装为HTTP API服务
这是最通用的落地方案,Web前端通过HTTP请求调用Bot接口获取落子结果,无需修改原有C算法逻辑。
步骤1:为C Bot添加网络服务层
选用轻量级C语言HTTP库(如libmicrohttpd、mongoose)快速搭建HTTP服务,核心是定义API接口并处理请求:
- 定义POST接口
/api/reversi/move,请求体以JSON格式传递当前棋盘状态(例:{"board": [[0,0,1,...], ...], "currentPlayer": 1},0为空、1为黑、2为白) - 在C服务中解析请求数据,调用已有的Bot算法计算最优落子,再返回JSON格式的落子坐标(例:
{"row": 3, "col": 4})
简化代码框架示例:
#include <stdio.h> #include <string.h> #include "microhttpd.h" #include "reversi_bot.h" // 你的Bot算法头文件 static int handle_post_request(void *cls, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **con_cls) { if (strcmp(url, "/api/reversi/move") != 0 || strcmp(method, "POST") != 0) { return MHD_NO; } // 这里需要实际解析上传的JSON数据,提取棋盘和玩家信息 int board[8][8] = {0}; // 示例棋盘数据 int current_player = 1; // 调用你的Bot算法计算落子 int row, col; bot_calculate_move(board, current_player, &row, &col); // 构造JSON响应 char response[100]; snprintf(response, sizeof(response), "{\"row\": %d, \"col\": %d}", row, col); struct MHD_Response *mhd_response = MHD_create_response_from_buffer(strlen(response), (void *)response, MHD_RESPMEM_PERSISTENT); // 添加CORS头解决跨域问题 MHD_add_response_header(mhd_response, "Access-Control-Allow-Origin", "*"); int ret = MHD_queue_response(connection, MHD_HTTP_OK, mhd_response); MHD_destroy_response(mhd_response); return ret; } int main() { struct MHD_Daemon *daemon = MHD_start_daemon(MHD_USE_INTERNAL_POLLING_THREAD, 8080, NULL, NULL, &handle_post_request, NULL, MHD_OPTION_END); if (!daemon) return 1; getchar(); MHD_stop_daemon(daemon); return 0; }
步骤2:Web前端调用API
在Web GUI中,当需要Bot落子时,用JavaScript发送POST请求:
async function requestBotMove(board, currentPlayer) { try { const response = await fetch('http://localhost:8080/api/reversi/move', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ board, currentPlayer }) }); const move = await response.json(); // 根据返回的row和col更新棋盘UI updateBoard(move.row, move.col, currentPlayer); } catch (err) { console.error('请求Bot失败:', err); } }
步骤3:部署注意事项
- 若前端与C服务不在同一域名/端口,需在C服务中添加CORS响应头(如上述代码中的
Access-Control-Allow-Origin) - 生产环境可将C服务部署在服务器,前端静态页面部署到Nginx等静态资源服务器,或用Nginx反向代理统一域名
方案二:将C Bot编译为WebAssembly(Wasm)
直接把C代码编译为Wasm,让Web前端在浏览器中直接调用,无需额外后端服务,延迟更低。
步骤1:用Emscripten编译C代码
安装Emscripten工具链后,编写暴露给JS的C接口:
#include "reversi_bot.h" // 暴露给JS的函数:接收一维棋盘数组、玩家,返回编码后的落子(row*8+col) int get_bot_move(int* board, int player) { int board_2d[8][8]; // 将一维数组转为Bot需要的二维格式 for (int i = 0; i < 8; i++) { for (int j = 0; j < 8; j++) { board_2d[i][j] = board[i*8 + j]; } } int row, col; bot_calculate_move(board_2d, player, &row, &col); return row * 8 + col; }
执行编译命令生成Wasm及JS桥接文件:
emcc reversi_bot.c -o reversi_bot.js -s EXPORTED_FUNCTIONS='["_get_bot_move", "_malloc", "_free"]' -s EXPORTED_RUNTIME_METHODS='["ccall", "cwrap"]'
步骤2:前端调用Wasm函数
在HTML中引入生成的reversi_bot.js,调用Bot函数:
// Wasm初始化完成后执行 Module.onRuntimeInitialized = function() { // 构造一维棋盘数组 const board = new Int32Array(64).fill(0); board[27] = 1; board[28] = 2; board[35] = 2; board[36] = 1; const currentPlayer = 1; // 分配内存存储棋盘数据 const ptr = Module._malloc(board.length * 4); Module.HEAP32.set(board, ptr / 4); // 调用C函数 const result = Module._get_bot_move(ptr, currentPlayer); // 解析落子坐标 const row = Math.floor(result / 8); const col = result % 8; // 释放内存 Module._free(ptr); console.log(`Bot落子:行${row},列${col}`); updateBoard(row, col, currentPlayer); };
方案三:用Node.js作为中间层
若熟悉Node.js,可将C Bot编译为Node.js原生模块,再用Node.js搭建API服务,兼顾开发效率与性能。
步骤1:编译C代码为Node.js模块
编写binding.gyp配置文件:
{ "targets": [ { "target_name": "reversi_bot", "sources": ["reversi_bot.c", "addon.c"] } ] }
编写addon.c暴露接口给Node.js:
#include <node.h> #include "reversi_bot.h" namespace reversi { using v8::FunctionCallbackInfo; using v8::Isolate; using v8::Local; using v8::Object; using v8::Number; using v8::Value; void GetBotMove(const FunctionCallbackInfo<Value>& args) { Isolate* isolate = args.GetIsolate(); // 解析传入的棋盘数组和玩家 Local<Object> boardObj = args[0].As<Object>(); int player = args[1].As<Number>()->Value(); int board[8][8]; // 从JS对象提取棋盘数据 for (int i = 0; i < 8; i++) { Local<Object> rowObj = boardObj->Get(isolate, i).As<Object>(); for (int j = 0; j < 8; j++) { board[i][j] = rowObj->Get(isolate, j).As<Number>()->Value(); } } int row, col; bot_calculate_move(board, player, &row, &col); // 返回落子结果 Local<Object> result = Object::New(isolate); result->Set(isolate, "row", Number::New(isolate, row)); result->Set(isolate, "col", Number::New(isolate, col)); args.GetReturnValue().Set(result); } void Initialize(Local<Object> exports) { NODE_SET_METHOD(exports, "getBotMove", GetBotMove); } NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize) }
编译模块:
node-gyp configure build
步骤2:搭建Node.js API服务
用Express快速实现接口:
const express = require('express'); const reversiBot = require('./build/Release/reversi_bot'); const app = express(); app.use(express.json()); app.post('/api/reversi/move', (req, res) => { const { board, currentPlayer } = req.body; const move = reversiBot.getBotMove(board, currentPlayer); res.json(move); }); app.listen(3000, () => { console.log('服务运行在端口3000'); });
前端同样通过fetch请求该接口即可。
内容的提问来源于stack exchange,提问作者agrimshar
相关产品推荐
相关产品推荐

