Web: Change to a webworker architecture to stop freezing main thread
This commit is contained in:
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
codegen
|
||||||
|
chess.wasm
|
||||||
|
mbb_bishop.h
|
||||||
|
mbb_rook.h
|
||||||
|
tests
|
||||||
|
*.dSYM
|
||||||
|
between_lookup.h
|
||||||
26
chess-worker.js
Normal file
26
chess-worker.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
let exports;
|
||||||
|
|
||||||
|
async function init() {
|
||||||
|
const resp = await fetch("./chess.wasm");
|
||||||
|
if (!resp.ok) {
|
||||||
|
throw new Error("fetch wasm failed ${resp.status} ${resp.statusText}");
|
||||||
|
}
|
||||||
|
|
||||||
|
const { instance } =
|
||||||
|
await WebAssembly.instantiateStreaming(resp, {});
|
||||||
|
|
||||||
|
exports = instance.exports;
|
||||||
|
}
|
||||||
|
await init();
|
||||||
|
self.postMessage({ type: "ready" });
|
||||||
|
|
||||||
|
self.onmessage = (e) => {
|
||||||
|
const { id, method, args = [] } = e.data;
|
||||||
|
try {
|
||||||
|
const value = exports[method](...args);
|
||||||
|
self.postMessage({ id, ok: true, value });
|
||||||
|
} catch (err) {
|
||||||
|
self.postMessage({ id, ok: false, error: String(err?.message ?? err) });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
44
chess.html
44
chess.html
@@ -5,31 +5,37 @@
|
|||||||
<title>Wasm Loader</title>
|
<title>Wasm Loader</title>
|
||||||
<script type="module" src="./chess.js" defer></script>
|
<script type="module" src="./chess.js" defer></script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
#board {
|
#board {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(8, 60px);
|
grid-template-columns: repeat(8, 60px);
|
||||||
grid-template-rows: repeat(8, 60px);
|
grid-template-rows: repeat(8, 60px);
|
||||||
width: 480px;
|
width: 480px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.square {
|
.square {
|
||||||
width: 60px;
|
width: 60px;
|
||||||
height: 60px;
|
height: 60px;
|
||||||
font-size: 57px;
|
font-size: 55px;
|
||||||
}
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.square.light {
|
.square.light {
|
||||||
background: #f0d9b5;
|
background: #f0d9b5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.square.dark {
|
.square.dark {
|
||||||
background: #b58863;
|
background: #b58863;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.square.blue {
|
||||||
|
background: blue;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="board"></div>
|
<div id="board"></div>
|
||||||
|
<p id="result"></div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
343
chess.js
343
chess.js
@@ -1,39 +1,16 @@
|
|||||||
|
|
||||||
const WasmHost = (() => {
|
|
||||||
async function load(url, imports = {}) {
|
|
||||||
const res = await fetch(url);
|
|
||||||
if (!res.ok) {
|
|
||||||
throw new Error(`Failed to fetch ${url}: ${res.status} ${res.statusText}`);
|
|
||||||
}
|
|
||||||
const bytes = await res.arrayBuffer();
|
|
||||||
const result = await WebAssembly.instantiate(bytes, imports);
|
|
||||||
const instance = result.instance ?? result;
|
|
||||||
return { instance, exports: instance.exports };
|
|
||||||
}
|
|
||||||
|
|
||||||
function getFn(exportsObj, name) {
|
|
||||||
const fn = exportsObj[name];
|
|
||||||
if (typeof fn !== "function") {
|
|
||||||
throw new Error(`Export "${name}" is not a function`);
|
|
||||||
}
|
|
||||||
return fn;
|
|
||||||
}
|
|
||||||
|
|
||||||
return { load, getFn };
|
|
||||||
})();
|
|
||||||
|
|
||||||
const MoveResult = Object.freeze({
|
const MoveResult = Object.freeze({
|
||||||
Normal: 0,
|
NORMAL: 0,
|
||||||
Check: 1,
|
CHECK: 1,
|
||||||
Repeats: 2,
|
REPEATS: 2,
|
||||||
Stalemate: 3,
|
STALEMATE: 3,
|
||||||
Checkmate: 4,
|
CHECKMATE: 4,
|
||||||
});
|
});
|
||||||
|
|
||||||
const _moveResultName = [
|
const _moveResultName = [
|
||||||
"Normal",
|
"Normal",
|
||||||
"Check",
|
"Check",
|
||||||
"Repeats",
|
"Repeats",
|
||||||
"Stalemate",
|
"Stalemate",
|
||||||
"Checkmate",
|
"Checkmate",
|
||||||
];
|
];
|
||||||
@@ -41,176 +18,248 @@ const _moveResultName = [
|
|||||||
const Player = Object.freeze({
|
const Player = Object.freeze({
|
||||||
White: 0,
|
White: 0,
|
||||||
Black: 1,
|
Black: 1,
|
||||||
None: 2,
|
None: 2,
|
||||||
});
|
});
|
||||||
|
|
||||||
const _playerName = [
|
const _playerName = [
|
||||||
"White",
|
"White",
|
||||||
"Black",
|
"Black",
|
||||||
"None",
|
"None",
|
||||||
];
|
];
|
||||||
|
|
||||||
const Piece = Object.freeze({
|
const Piece = Object.freeze({
|
||||||
Pawn: 0,
|
Pawn: 0,
|
||||||
King: 1,
|
King: 1,
|
||||||
Queen: 2,
|
Queen: 2,
|
||||||
Bishop: 3,
|
Bishop: 3,
|
||||||
Rook: 4,
|
Rook: 4,
|
||||||
Knight: 5,
|
Knight: 5,
|
||||||
Empty: 6,
|
Empty: 6,
|
||||||
});
|
});
|
||||||
|
|
||||||
const _pieceName = [
|
const _pieceName = [
|
||||||
"Pawn",
|
"Pawn",
|
||||||
"King",
|
"King",
|
||||||
"Queen",
|
"Queen",
|
||||||
"Bishop",
|
"Bishop",
|
||||||
"Rook",
|
"Rook",
|
||||||
"Knight",
|
"Knight",
|
||||||
"Empty",
|
"Empty",
|
||||||
];
|
];
|
||||||
|
|
||||||
const _pieceChar = [
|
const _pieceChar = [
|
||||||
'P',
|
'P',
|
||||||
'K',
|
'K',
|
||||||
'Q',
|
'Q',
|
||||||
'B',
|
'B',
|
||||||
'R',
|
'R',
|
||||||
'N',
|
'N',
|
||||||
' ',
|
' ',
|
||||||
];
|
];
|
||||||
|
|
||||||
const _pieceUnicode = [
|
const _pieceUnicode = [
|
||||||
['♙', '♔', '♕', '♗', '♖', '♘',], // white
|
['♙', '♔', '♕', '♗', '♖', '♘',], // white
|
||||||
['♟', '♚', '♛', '♝', '♜', '♞',], // black
|
['♟', '♚', '♛', '♝', '♜', '♞',], // black
|
||||||
['' , '', '', '', '', '', ], // none
|
['' , '', '', '', '', '', ], // none
|
||||||
];
|
];
|
||||||
|
|
||||||
const indexDeserialize = (n) => ({
|
const indexDeserialize = (n) => ({
|
||||||
rank: Math.floor(n / 8), // every day we stray further from God
|
rank: Math.floor(n / 8), // every day we stray further from God
|
||||||
file: (n % 8),
|
file: (n % 8),
|
||||||
fileChar() { return ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'][this.file]; },
|
fileChar() { return ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'][this.file]; },
|
||||||
rankChar() { return ['1', '2', '3', '4', '5', '6', '7', '8'][this.rank]; },
|
rankChar() { return ['1', '2', '3', '4', '5', '6', '7', '8'][this.rank]; },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
const indexFromCoord = (rank, file) => (8*rank + file);
|
const serializedIndexFromCoord = (rank, file) => (8*rank + file);
|
||||||
|
|
||||||
const indexSerialize = (index) => (indexFromCoord(index.rank, index.file));
|
const indexSerialize = (index) => (serializedIndexFromCoord(index.rank, index.file));
|
||||||
|
|
||||||
const moveDeserialize = (n) => ({
|
const moveDeserialize = (n) => ({
|
||||||
appeal: Number((n >> 24n) & 0xFFn),
|
appeal: Number((n >> 24n) & 0xFFn),
|
||||||
attr: Number((n >> 16n) & 0xFFn),
|
attr: Number((n >> 16n) & 0xFFn),
|
||||||
from: indexDeserialize(Number((n >> 8n) & 0xFFn)),
|
from: indexDeserialize(Number((n >> 8n) & 0xFFn)),
|
||||||
to: indexDeserialize(Number((n >> 0n) & 0xFFn)),
|
to: indexDeserialize(Number((n >> 0n) & 0xFFn)),
|
||||||
});
|
});
|
||||||
|
|
||||||
const moveSerialize = (move) => ((move.from & 0xFF) << 8) | ((move.to & 0xFF));
|
const moveSerialize = (move) => (
|
||||||
|
(indexSerialize(move.from) & 0xFF) << 8) | ((indexSerialize(move.to) & 0xFF));
|
||||||
|
|
||||||
const squareDeserialize = (n) => ({
|
const squareDeserialize = (n) => ({
|
||||||
player: n == -1 ? Player.None : (n >> 8) & 0xFF,
|
player: n == -1 ? Player.None : (n >> 8) & 0xFF,
|
||||||
piece: n == -1 ? Piece.Empty : (n & 0xFF),
|
piece: n == -1 ? Piece.Empty : (n & 0xFF),
|
||||||
playerName() {
|
playerName() {
|
||||||
if (this.player === Player.None) {
|
if (this.player === Player.None) {
|
||||||
return "Empty";
|
return "Empty";
|
||||||
} else {
|
} else {
|
||||||
return _playerName[this.player];
|
return _playerName[this.player];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
pieceName() {
|
pieceName() {
|
||||||
if (this.player === Player.None) {
|
if (this.player === Player.None) {
|
||||||
return "";
|
return "";
|
||||||
} else {
|
} else {
|
||||||
return this.playerName() + _pieceName[this.piece];
|
return this.playerName() + _pieceName[this.piece];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
pieceUnicode() {
|
pieceUnicode() {
|
||||||
return _pieceUnicode[this.player][this.piece];
|
return _pieceUnicode[this.player][this.piece];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const WasmBoard = async (url) => {
|
||||||
|
/*
|
||||||
|
const res = await fetch(url);
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error(`Failed to fetch ${url}: ${res.status} ${res.statusText}`);
|
||||||
|
}
|
||||||
|
const bytes = await res.arrayBuffer();
|
||||||
|
const instantiated = await WebAssembly.instantiate(bytes, {});
|
||||||
|
const instance = instantiated.instance ?? instantiated;
|
||||||
|
const exports = instance.exports;
|
||||||
|
|
||||||
|
const wb_init = exports.wb_init;
|
||||||
|
const wb_move = exports.wb_move;
|
||||||
|
const wb_search = exports.wb_search;
|
||||||
|
const wb_board_at = exports.wb_board_at;
|
||||||
|
*/
|
||||||
|
const worker = new Worker("./chess-worker.js", { type: "module" });
|
||||||
|
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
worker.addEventListener("message", (e) => {
|
||||||
|
if (e.data?.type === "ready") {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
}, { once: true });
|
||||||
|
worker.addEventListener("error", reject, { once: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
const wasmCall = (method, args = []) => new Promise((resolve, reject) => {
|
||||||
|
const id = crypto.randomUUID();
|
||||||
|
const onMessage = (e) => {
|
||||||
|
if (e.data?.id !== id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cleanup();
|
||||||
|
if (e.data.ok) {
|
||||||
|
resolve(e.data.value);
|
||||||
|
} else {
|
||||||
|
reject(new Error(e.data.error));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onError = (err) => {
|
||||||
|
cleanup();
|
||||||
|
reject(err);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onMessageError = (e) => {
|
||||||
|
cleanup();
|
||||||
|
reject(new Error("Worker message deserialization failed (messageerror)."));
|
||||||
|
};
|
||||||
|
|
||||||
|
const cleanup = () => {
|
||||||
|
worker.removeEventListener("message", onMessage);
|
||||||
|
worker.removeEventListener("error", onError);
|
||||||
|
worker.removeEventListener("messageerror", onMessageError);
|
||||||
|
};
|
||||||
|
|
||||||
|
worker.addEventListener("message", onMessage);
|
||||||
|
worker.addEventListener("error", onError);
|
||||||
|
worker.addEventListener("messageerror", onMessageError);
|
||||||
|
|
||||||
|
worker.postMessage({ id, method, args });
|
||||||
|
});
|
||||||
|
|
||||||
|
await wasmCall("wb_init", []);
|
||||||
|
|
||||||
|
return {
|
||||||
|
state: MoveResult.Normal,
|
||||||
|
|
||||||
|
ongoing: function() /* nil -> bool */ {
|
||||||
|
return (this.state != MoveResult.STALEMATE
|
||||||
|
&& this.state != MoveResult.CHECKMATE);
|
||||||
|
},
|
||||||
|
|
||||||
|
search: async function(depth) /* Int -> Move */ {
|
||||||
|
return wasmCall("wb_search", [depth])
|
||||||
|
.then(moveDeserialize);
|
||||||
|
},
|
||||||
|
|
||||||
|
move: async function (move) /* (Move | SerializedMove) -> MoveResult */ {
|
||||||
|
const m = (typeof move === "number") ? move : moveSerialize(move);
|
||||||
|
return wasmCall("wb_move", [m]);
|
||||||
|
},
|
||||||
|
|
||||||
|
at: async function (index) /* (Index | SerializedIndex) -> Square */ {
|
||||||
|
const i = (typeof index === "number") ? index : indexSerialize(index);
|
||||||
|
return wasmCall("wb_board_at", [i])
|
||||||
|
.then(squareDeserialize);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
const run = async () => {
|
const run = async () => {
|
||||||
const { exports } = await WasmHost.load("./chess.wasm", {});
|
const board = await WasmBoard("./chess-worker.js");
|
||||||
|
|
||||||
const wb_init = WasmHost.getFn(exports, "wb_init");
|
const nextFrame = () => new Promise(requestAnimationFrame);
|
||||||
const wb_move = WasmHost.getFn(exports, "wb_move");
|
|
||||||
const wb_search = WasmHost.getFn(exports, "wb_search");
|
|
||||||
const wb_board_at = WasmHost.getFn(exports, "wb_board_at");
|
|
||||||
|
|
||||||
wb_init();
|
const createBoardUI = async (board) => {
|
||||||
|
const boardElem = document.getElementById("board");
|
||||||
|
boardElem.replaceChildren();
|
||||||
|
const filesChars = ["a","b","c","d","e","f","g","h"];
|
||||||
|
const ranksChars = ["8","7","6","5","4","3","2","1"];
|
||||||
|
|
||||||
const drawBoard = () => {
|
const squares = new Array(64);
|
||||||
const board = document.getElementById("board");
|
|
||||||
board.replaceChildren();
|
|
||||||
const filesChars = ["a","b","c","d","e","f","g","h"];
|
|
||||||
const ranksChars = ["8","7","6","5","4","3","2","1"];
|
|
||||||
|
|
||||||
for (let rank = 7; rank >= 0; --rank) {
|
for (let rank = 7; rank >= 0; --rank) {
|
||||||
for (let file = 0; file < 8; ++file) {
|
for (let file = 0; file < 8; ++file) {
|
||||||
const square = document.createElement("div");
|
const el = document.createElement("div");
|
||||||
square.className =
|
el.className =
|
||||||
"square " + ((file + rank) % 2 ? "dark" : "light");
|
"square " + ((file + rank) % 2 ? "dark" : "light");
|
||||||
square.dataset.file = filesChars[file];
|
el.dataset.file = filesChars[file];
|
||||||
square.dataset.rank = ranksChars[rank];
|
el.dataset.rank = ranksChars[rank];
|
||||||
|
|
||||||
const x = wb_board_at(indexFromCoord(rank, file));
|
const sq = await board.at(serializedIndexFromCoord(rank, file));
|
||||||
const sq = squareDeserialize(x);
|
el.textContent = sq.pieceUnicode();
|
||||||
square.textContent = sq.pieceUnicode();
|
|
||||||
|
|
||||||
board.appendChild(square);
|
boardElem.appendChild(el);
|
||||||
}
|
squares[8*rank+file] = el;
|
||||||
}
|
}
|
||||||
setTimeout(() => {
|
}
|
||||||
// next task, browser had a chance to repaint
|
|
||||||
}, 0.01);
|
|
||||||
}
|
|
||||||
drawBoard();
|
|
||||||
|
|
||||||
const nextFrame = () => new Promise(requestAnimationFrame);
|
return squares;
|
||||||
|
};
|
||||||
|
|
||||||
for (let i = 0; i < 200; ++i) {
|
const draw = async (board, squareElems) => {
|
||||||
drawBoard();
|
for (let rank = 7; rank >= 0; --rank) {
|
||||||
|
for (let file = 0; file < 8; ++file) {
|
||||||
|
const sq = await board.at(serializedIndexFromCoord(rank, file));
|
||||||
|
squareElems[8*rank+file].textContent = sq.pieceUnicode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
await nextFrame();
|
const sqElems = await createBoardUI(board);
|
||||||
|
for (let i = 0; i < 200; ++i) {
|
||||||
|
await draw(board, sqElems);
|
||||||
|
await nextFrame();
|
||||||
|
|
||||||
const m = wb_search(7);
|
const move = await board.search(7);
|
||||||
const move = moveDeserialize(m);
|
|
||||||
|
|
||||||
console.log(move.from);
|
const fromSq = await board.at(move.from);
|
||||||
console.log(move.to);
|
const toSq = await board.at(move.to);
|
||||||
|
|
||||||
const fromSqEnc = wb_board_at(indexSerialize(move.from));
|
const mr = board.move(move);
|
||||||
const toSqEnc = wb_board_at(indexSerialize(move.to));
|
if (mr == MoveResult.STALEMATE || mr == MoveResult.CHECKMATE) {
|
||||||
|
const resultEl = document.getElementById("result");
|
||||||
console.log(fromSqEnc);
|
resultEl.textContent = _moveResultName[board.state];
|
||||||
console.log(toSqEnc);
|
draw(board, sqElems);
|
||||||
|
await nextFrame();
|
||||||
const fromSq = squareDeserialize(fromSqEnc);
|
break;
|
||||||
const toSq = squareDeserialize(toSqEnc);
|
}
|
||||||
|
}
|
||||||
console.log(fromSq)
|
|
||||||
console.log(toSq)
|
|
||||||
|
|
||||||
console.log("from-player:", fromSq.playerName());
|
|
||||||
console.log("from-piece:", fromSq.pieceName());
|
|
||||||
|
|
||||||
console.log("to-player:", toSq.playerName());
|
|
||||||
console.log("to-piece:", toSq.pieceName());
|
|
||||||
|
|
||||||
console.log(m);
|
|
||||||
const f = move.from;
|
|
||||||
const t = move.to;
|
|
||||||
console.log("from:", move.from.fileChar(), move.from.rankChar(),
|
|
||||||
"to:", move.to.fileChar(), move.to.rankChar());
|
|
||||||
|
|
||||||
const mr = wb_move(Number(m));
|
|
||||||
if (mr == MoveResult.Stalemate || mr == MoveResult.Checkmate) {
|
|
||||||
console.log(_moveResultName[mr]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
run().catch((err) => {
|
run().catch((err) => {
|
||||||
|
|||||||
10
libc-lite.h
10
libc-lite.h
@@ -7,12 +7,12 @@
|
|||||||
|
|
||||||
static uint64_t g_rand_seed = 1;
|
static uint64_t g_rand_seed = 1;
|
||||||
|
|
||||||
void my_srand(uint64_t n)
|
static void my_srand(uint64_t n)
|
||||||
{
|
{
|
||||||
g_rand_seed = n == 0 ? 1 : n;
|
g_rand_seed = n == 0 ? 1 : n;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t my_rand64()
|
static uint64_t my_rand64()
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Xorshift RNG [1] + multiply
|
* Xorshift RNG [1] + multiply
|
||||||
@@ -37,7 +37,7 @@ static int my_isdigit(int ch)
|
|||||||
return ch >= '0' && ch <= '9';
|
return ch >= '0' && ch <= '9';
|
||||||
}
|
}
|
||||||
|
|
||||||
void* my_memset(void *b, int c, size_t len)
|
static void* my_memset(void *b, int c, size_t len)
|
||||||
{
|
{
|
||||||
unsigned char* x = b;
|
unsigned char* x = b;
|
||||||
unsigned char ch = (unsigned char)c;
|
unsigned char ch = (unsigned char)c;
|
||||||
@@ -47,7 +47,7 @@ void* my_memset(void *b, int c, size_t len)
|
|||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
void* my_memcpy(void* restrict dst, void const* restrict src, size_t n)
|
static void* my_memcpy(void* restrict dst, void const* restrict src, size_t n)
|
||||||
{
|
{
|
||||||
uint8_t* d = dst;
|
uint8_t* d = dst;
|
||||||
uint8_t const* s = src;
|
uint8_t const* s = src;
|
||||||
@@ -73,7 +73,7 @@ int my_memcmp(void const* s1, void const* s2, size_t n)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t strlen(char const* s)
|
static size_t strlen(char const* s)
|
||||||
{
|
{
|
||||||
size_t n = 0;
|
size_t n = 0;
|
||||||
while (s[n++])
|
while (s[n++])
|
||||||
|
|||||||
7
sys.h
7
sys.h
@@ -15,14 +15,13 @@ enum { /* TODO */
|
|||||||
SYS_MADV_SEQUENTIAL = 0,
|
SYS_MADV_SEQUENTIAL = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
static uint8_t g_buf[1024*1024*1024];
|
static uint8_t g_buf[1024*1024*1024 /* 1 GB */];
|
||||||
static size_t g_buf_len = 0;
|
static size_t g_buf_len = 0;
|
||||||
|
|
||||||
static void* sys_mmap_anon_shared(size_t size, int, int)
|
static void* sys_mmap_anon_shared(size_t size, int, int)
|
||||||
{
|
{
|
||||||
/* FIXME: this program relies on few memory allocations, a simple bump
|
/* FIXME: this program relies on very few memory allocations, a simple bump
|
||||||
* allocator works for now, but will cause memory leaks in the future */
|
* allocator works for now, but will cause memory leaks in the future */
|
||||||
|
|
||||||
size = (size + 7ULL) & ~7ULL;
|
size = (size + 7ULL) & ~7ULL;
|
||||||
|
|
||||||
if (g_buf_len + size > sizeof g_buf) {
|
if (g_buf_len + size > sizeof g_buf) {
|
||||||
|
|||||||
@@ -40,9 +40,21 @@ uint64_t wb_search(int8_t max_depth)
|
|||||||
|
|
||||||
int32_t wb_move(uint32_t move)
|
int32_t wb_move(uint32_t move)
|
||||||
{
|
{
|
||||||
struct move m = move_deserialize(move);
|
struct move const m = move_deserialize(move);
|
||||||
enum move_result const r = board_move_2(&g_board, m);
|
enum move_result const mr = board_move_2(&g_board, m);
|
||||||
return (int32_t)r;
|
|
||||||
|
/* TODO: this checkmate/stalemate check needs to be abstracted better */
|
||||||
|
if (mr == MR_STALEMATE) {
|
||||||
|
return (int32_t)MR_STALEMATE;
|
||||||
|
}
|
||||||
|
struct move moves[MOVE_MAX];
|
||||||
|
size_t move_count = 0ULL;
|
||||||
|
all_moves(&g_board.pos, opposite_player(g_board.pos.player), &move_count, moves);
|
||||||
|
if (move_count == 0ULL) {
|
||||||
|
return MR_CHECKMATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int32_t)mr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void wb_init()
|
void wb_init()
|
||||||
|
|||||||
Reference in New Issue
Block a user