A headless Makruk (Thai chess) library. No UI included - this is a pure game logic engine.
Live Demo with UI: makruk-js.com
- Headless - Pure game logic, no UI dependencies
- Fast - Bitboard representation with bitwise operations
- Immutable - All functions return new state objects
- TypeScript - Full type definitions included
- Zero Dependencies - Only peer dependency is TypeScript
pnpm add @kaisukez/makruk-jsimport { createInitialState, move, generateLegalMoves, isGameOver } from '@kaisukez/makruk-js'
const state = createInitialState()
const newState = move(state, 'Me2')
const moves = generateLegalMoves(newState)
console.log(`${moves.length} legal moves`)
console.log(isGameOver(newState))import { move, generateLegalMoves } from '@kaisukez/makruk-js'
// Move using SAN notation
state = move(state, 'Me2')
// Move using MoveObject
const moves = generateLegalMoves(state)
state = move(state, moves[0])import { isCheck, isCheckmate, isStalemate, isDraw, isGameOver } from '@kaisukez/makruk-js'
isCheck(state) // Is current player in check?
isCheckmate(state) // Is it checkmate?
isStalemate(state) // Is it stalemate?
isDraw(state) // Any type of draw?
isGameOver(state) // Game ended?import { createInitialState, createGameFromFen, exportFen, INITIAL_FEN, EMPTY_FEN } from '@kaisukez/makruk-js'
// Create initial state (preferred)
const state = createInitialState()
// Or create from FEN string
const customState = createGameFromFen('4k3/8/8/8/8/8/8/4K3 w 1')
const fen = exportFen(customState)import { importPgn, exportPgnFromHistory, parsePgn, exportPgn } from '@kaisukez/makruk-js'
// Import PGN to game states
const states = importPgn(pgnString)
// Export game history to PGN
const pgn = exportPgnFromHistory(states, { Event: 'Game', White: 'Player1', Black: 'Player2' })
// Low-level: parse/export PGN structure
const game = parsePgn(pgnString)
const pgnOutput = exportPgn(game)import { findBestMove, iterativeDeepening, minimax, evaluate } from '@kaisukez/makruk-js'
// Find best move at fixed depth
const { bestMove, bestScore } = findBestMove(state, 3)
if (bestMove) {
state = move(state, bestMove)
}
// Iterative deepening with time limit (recommended)
const result = iterativeDeepening(state, 5, 3000) // max depth 5, 3 seconds
// Direct minimax with alpha-beta
const result = minimax(state, depth, -Infinity, Infinity)
// Position evaluation
const score = evaluate(state)For web workers or Node.js worker threads:
import {
searchMoves,
distributeMoves,
combineResults,
generateLegalMoves,
getRecommendedWorkers,
createTranspositionTable,
Color
} from '@kaisukez/makruk-js'
// Get number of workers based on CPU cores
const numWorkers = getRecommendedWorkers() // Node.js only
// For browser: navigator.hardwareConcurrency - 1
// Distribute moves across workers
const moves = generateLegalMoves(state)
const moveBuckets = distributeMoves(moves, numWorkers)
// Each worker creates its own transposition table and searches its assigned moves
const tt = createTranspositionTable()
const result = searchMoves(state, moveBuckets[workerIndex], depth, tt)
// Combine results from all workers
const combined = combineResults(workerResults, state.turn === Color.WHITE)import { put, remove, Color, Piece, SquareIndex } from '@kaisukez/makruk-js'
state = put(state, Color.WHITE, Piece.RUA, SquareIndex.d4)
state = remove(state, SquareIndex.d4)import type { Game, Move, MinimaxOutput, PgnGame, PgnMove } from '@kaisukez/makruk-js'
import { Color, Piece, PIECE_POWER } from '@kaisukez/makruk-js'interface Game {
board: Board // Internal board representation
turn: Color // Current player
moveNumber: number // Current move number
fen: string // Current FEN
countdown: object // Makruk counting rules state
fenOccurrence: object // Position repetition tracking
}interface Move {
from: number // Source square (0-63)
to: number // Target square (0-63)
piece: Piece
color: Color
captured?: Piece
promotion?: Piece
san: string
flags: { normal: boolean, capture: boolean, promotion: boolean }
}| Symbol | Name | Thai | Movement |
|---|---|---|---|
| K/k | Khun (King) | ขุน | One square any direction |
| E/e | Met (Queen) | เม็ด | One square diagonally |
| R/r | Rua (Rook) | เรือ | Any squares horizontally/vertically |
| M/m | Ma (Knight) | ม้า | L-shape |
| T/t | Thon (Bishop) | โคน | One square diagonally forward |
| B/b | Bia (Pawn) | เบี้ยคว่ำ | One square forward |
| F/f | Flipped Bia | เบี้ยหงาย | One square forward or diagonally forward |
Capital = White, lowercase = Black.
Makruk has special endgame rules. When one side has only the King left, the other side must checkmate within a limited number of moves. The countdown property tracks this automatically.
A deprecated array-based implementation is available for backward compatibility:
import { importFen, move, printBoard } from '@kaisukez/makruk-js/0x88'See 0x88 README for details.
pnpm install # Install dependencies
pnpm test # Run tests
pnpm build # BuildMIT