YATT - Yet Another Turing Test

Discussion of anything and everything relating to chess playing software and machines.

Moderator: Ras

smatovic
Posts: 3297
Joined: Wed Mar 10, 2010 10:18 pm
Location: Hamburg, Germany
Full name: Srdja Matovic

YATT - Yet Another Turing Test

Post by smatovic »

...to collect already posted stuff into one thread:

Now with context generative AIs, the switch from pattern recognition to pattern creation with neural networks, I would like to propose my own kind of Turing Test:

An AI which is able to code a chess engine and outperforms humans in this task.

1A) With hand-crafted eval. 1B) With neural networks.

2A) Outperforms non-programmers. 2B) Outperforms average chess-programmers. 2C) Outperforms top chess-programmers.

3A) An un-self-aware AI, the "RI", restricted intelligence. 2B) A self-aware AI, the "SI", sentient intelligence.

4A) An AI based on expert-systems. 4B) An AI based on neural networks. 4C) A merger of both.

The Chinese Room Argument applied onto this test would claim that there is no conscious in need to perform such a task, hence this test is not meant to measure self-awareness, consciousness or sentience, but what we call human intelligence.

https://en.wikipedia.org/wiki/Chinese_room

--
Srdja
smatovic
Posts: 3297
Joined: Wed Mar 10, 2010 10:18 pm
Location: Hamburg, Germany
Full name: Srdja Matovic

Re: YATT - Yet Another Turing Test

Post by smatovic »

The first test candidate was posted by Thomas Zipproth, December 08, 2022, generated by GPT-3:

Provide me with a minimal working source code of a chess engine
forum3/viewtopic.php?f=2&t=81097&start=20#p939245

Code: Select all

import chess

board = chess.Board()

while not board.is_game_over():
# Generate a list of all legal moves
legal_moves = list(board.legal_moves)

# Choose a random move from the list of legal moves
move = random.choice(legal_moves)

# Make the move on the board
board.push(move)

# Print the final game state
print(board)
It is a random mover which imports a chess library for move generation and game states.

--
Srdja
smatovic
Posts: 3297
Joined: Wed Mar 10, 2010 10:18 pm
Location: Hamburg, Germany
Full name: Srdja Matovic

Re: YATT - Yet Another Turing Test

Post by smatovic »

Second test candidate was posted by Darko Markovic, June 08, 2024, generated by GPT-4o:

GPT-4o made a chess engine
viewtopic.php?t=83882

Code: Select all

import chess
import chess.pgn
import chess.polyglot
import os

# Piece values with slight positional bonuses
PIECE_VALUES = {
    chess.PAWN: 100,
    chess.KNIGHT: 320,
    chess.BISHOP: 330,
    chess.ROOK: 500,
    chess.QUEEN: 900,
    chess.KING: 20000
}

# Central squares and other positional factors
CENTER_SQUARES = [chess.E4, chess.D4, chess.E5, chess.D5]
KING_SAFETY_PENALTY = 50
PAWN_STRUCTURE_BONUS = 20
MOBILITY_BONUS = 5

def evaluate_board(board):
    if board.is_checkmate():
        return -PIECE_VALUES[chess.KING] if board.turn == chess.WHITE else PIECE_VALUES[chess.KING]
    if board.is_stalemate() or board.is_insufficient_material():
        return 0
    
    value = 0
    for piece_type in PIECE_VALUES:
        value += len(board.pieces(piece_type, chess.WHITE)) * PIECE_VALUES[piece_type]
        value -= len(board.pieces(piece_type, chess.BLACK)) * PIECE_VALUES[piece_type]
    
    value += evaluate_pawn_structure(board)
    value += evaluate_king_safety(board)
    value += evaluate_control_of_center(board)
    value += evaluate_piece_mobility(board)
    
    return value

def evaluate_pawn_structure(board):
    score = 0
    for color in [chess.WHITE, chess.BLACK]:
        pawns = board.pieces(chess.PAWN, color)
        for pawn in pawns:
            # Isolated pawns
            if not any(board.piece_at(chess.square(pawn % 8, r)) == chess.PAWN and board.color_at(chess.square(pawn % 8, r)) == color for r in range(8) if r != chess.square_rank(pawn)):
                score -= PAWN_STRUCTURE_BONUS if color == chess.WHITE else -PAWN_STRUCTURE_BONUS
            
            # Doubled pawns
            if len([p for p in pawns if chess.square_file(p) == chess.square_file(pawn)]) > 1:
                score -= PAWN_STRUCTURE_BONUS // 2 if color == chess.WHITE else -PAWN_STRUCTURE_BONUS // 2

            # Backward pawns
            if is_backward_pawn(board, pawn, color):
                score -= PAWN_STRUCTURE_BONUS // 3 if color == chess.WHITE else -PAWN_STRUCTURE_BONUS // 3
                
    return score

def is_backward_pawn(board, pawn_square, color):
    file = chess.square_file(pawn_square)
    rank = chess.square_rank(pawn_square)
    direction = 1 if color == chess.WHITE else -1
    backward = True
    
    for adj_file in [file - 1, file + 1]:
        if 0 <= adj_file < 8:
            adj_square = chess.square(adj_file, rank)
            if board.piece_at(adj_square) == chess.PAWN and board.color_at(adj_square) == color:
                backward = False
                break
    
    if backward:
        for forward_rank in range(rank + direction, 8 if color == chess.WHITE else -1, direction):
            forward_square = chess.square(file, forward_rank)
            if board.piece_at(forward_square) == chess.PAWN and board.color_at(forward_square) == color:
                backward = False
                break
            if (adj_file := file - 1) >= 0:
                adj_square = chess.square(adj_file, forward_rank)
                if board.piece_at(adj_square) == chess.PAWN and board.color_at(adj_square) != color:
                    backward = False
                    break
            if (adj_file := file + 1) < 8:
                adj_square = chess.square(adj_file, forward_rank)
                if board.piece_at(adj_square) == chess.PAWN and board.color_at(adj_square) != color:
                    backward = False
                    break

    return backward

def evaluate_king_safety(board):
    score = 0
    for color in [chess.WHITE, chess.BLACK]:
        king_square = board.king(color)
        king_rank = chess.square_rank(king_square)
        king_file = chess.square_file(king_square)
        
        pawns_around_king = 0
        for rank in range(max(0, king_rank - 1), min(7, king_rank + 1) + 1):
            for file in range(max(0, king_file - 1), min(7, king_file + 1) + 1):
                if board.piece_at(chess.square(file, rank)) == chess.PAWN and board.color_at(chess.square(file, rank)) == color:
                    pawns_around_king += 1
        
        score -= (3 - pawns_around_king) * KING_SAFETY_PENALTY if color == chess.WHITE else -(3 - pawns_around_king) * KING_SAFETY_PENALTY
    return score

def evaluate_control_of_center(board):
    score = 0
    for square in CENTER_SQUARES:
        if board.piece_at(square):
            piece = board.piece_at(square)
            if piece.color == chess.WHITE:
                score += PIECE_VALUES[piece.piece_type] * 0.1
            else:
                score -= PIECE_VALUES[piece.piece_type] * 0.1
    return score

def evaluate_piece_mobility(board):
    score = 0
    for color in [chess.WHITE, chess.BLACK]:
        for square in board.piece_map():
            piece = board.piece_at(square)
            if piece.color == color:
                if color == chess.WHITE:
                    score += len(board.attacks(square)) * MOBILITY_BONUS
                else:
                    score -= len(board.attacks(square)) * MOBILITY_BONUS
    return score

KILLER_MOVES = {}
HISTORY_HEURISTIC = {}

def order_moves(board, depth):
    captures = []
    non_captures = []
    for move in board.legal_moves:
        if board.is_capture(move):
            captures.append(move)
        else:
            non_captures.append(move)
    
    captures.sort(key=lambda move: PIECE_VALUES[board.piece_at(move.to_square).piece_type] if board.piece_at(move.to_square) else 0, reverse=True)
    
    # Killer move heuristic
    non_captures.sort(key=lambda move: (move in KILLER_MOVES.get(depth, []), HISTORY_HEURISTIC.get(move, 0)), reverse=True)
    
    return captures + non_captures

def register_killer_move(move, depth):
    if depth not in KILLER_MOVES:
        KILLER_MOVES[depth] = [move]
    elif move not in KILLER_MOVES[depth]:
        KILLER_MOVES[depth].append(move)
        if len(KILLER_MOVES[depth]) > 2:
            KILLER_MOVES[depth].pop(0)

def register_history_heuristic(move, depth):
    if move in HISTORY_HEURISTIC:
        HISTORY_HEURISTIC[move] += depth * depth
    else:
        HISTORY_HEURISTIC[move] = depth * depth

TRANSPOSITION_TABLE = {}

def alpha_beta_search(board, depth, alpha, beta):
    if depth == 0:
        return quiescence_search(board, alpha, beta)
    
    board_fen = board.fen()
    if board_fen in TRANSPOSITION_TABLE and TRANSPOSITION_TABLE[board_fen][0] >= depth:
        return TRANSPOSITION_TABLE[board_fen][1]
    
    best_score = -float('inf')
    
    for move in order_moves(board, depth):
        board.push(move)
        score = -alpha_beta_search(board, depth - 1, -beta, -alpha)
        board.pop()
        
        if score >= beta:
            register_killer_move(move, depth)
            TRANSPOSITION_TABLE[board_fen] = (depth, score)
            return score
        
        if score > alpha:
            alpha = score
            best_score = score
            register_history_heuristic(move, depth)
    
    TRANSPOSITION_TABLE[board_fen] = (depth, best_score)
    return best_score

def null_move_pruning(board, depth, alpha, beta):
    R = 2  # Reduction value for null move pruning
    if depth > R and not board.is_check():
        board.push(chess.Move.null())
        score = -alpha_beta_search(board, depth - 1 - R, -beta, -beta + 1)
        board.pop()
        if score >= beta:
            return beta
    return None

def quiescence_search(board, alpha, beta):
    stand_pat = evaluate_board(board)
    if stand_pat >= beta:
        return beta
    if alpha < stand_pat:
        alpha = stand_pat

    for move in board.legal_moves:
        if board.is_capture(move):
            board.push(move)
            score = -quiescence_search(board, -beta, -alpha)
            board.pop()

            if score >= beta:
                return beta
            if score > alpha:
                alpha = score

    return alpha

def search(board, depth):
    alpha = -float('inf')
    beta = float('inf')
    best_move = None
    best_score = -float('inf')

    for move in order_moves(board, depth):
        board.push(move)
        score = -alpha_beta_search(board, depth - 1, -beta, -alpha)
        board.pop()

        if score > best_score:
            best_score = score
            best_move = move

        # Print the search progress
        print(f"Depth: {depth}, Move: {move}, Eval: {score}")

    return best_move, best_score

def play_as_white():
    board = chess.Board()
    depth = 3
    game = chess.pgn.Game()
    game.headers["White"] = "Engine"
    game.headers["Black"] = "User"
    node = game

    with chess.polyglot.open_reader("book.bin") as reader:
        while not board.is_game_over():
            print(board)

            if board.turn == chess.WHITE:
                move = None
                try:
                    entry = reader.get(board)
                    move = entry.move
                except:
                    pass
                if not move:
                    move, score = search(board, depth)
                board.push(move)
                print(f"Engine move: {move}")
            else:
                user_move = input("Enter your move in UCI format (e.g., e2e4): ")

                try:
                    move = chess.Move.from_uci(user_move)
                    if move in board.legal_moves:
                        board.push(move)
                    else:
                        print("Illegal move, try again.")
                        continue
                except:
                    print("Invalid move format, try again.")
                    continue

            node = node.add_variation(move)

    print("Game over")
    print(board.result())

    # Save game to PGN file
    with open("games.pgn", "a") as pgn_file:
        print(game, file=pgn_file, end="\n\n")

def play_as_black():
    board = chess.Board()
    depth = 3
    game = chess.pgn.Game()
    game.headers["White"] = "User"
    game.headers["Black"] = "Engine"
    node = game

    with chess.polyglot.open_reader("book.bin") as reader:
        while not board.is_game_over():
            print(board)

            if board.turn == chess.WHITE:
                user_move = input("Enter your move in UCI format (e.g., e2e4): ")

                try:
                    move = chess.Move.from_uci(user_move)
                    if move in board.legal_moves:
                        board.push(move)
                    else:
                        print("Illegal move, try again.")
                        continue
                except:
                    print("Invalid move format, try again.")
                    continue
            else:
                move = None
                try:
                    entry = reader.get(board)
                    move = entry.move
                except:
                    pass
                if not move:
                    move, score = search(board, depth)
                board.push(move)
                print(f"Engine move: {move}")

            node = node.add_variation(move)

    print("Game over")
    print(board.result())

    # Save game to PGN file
    with open("games.pgn", "a") as pgn_file:
        print(game, file=pgn_file, end="\n\n")

# Uncomment the desired function to play as white or black
# play_as_white()
play_as_black()
- imports chess library for move generation, game states and PGN export
- AlphaBeta search as Negamax
- Move Sorting with captures before non-captures and captures as MVV
- Quiescence Search with Stand Pat
- wood+pawn-structure+king-savety+center+mobility evaluation
- Transposition Tables
- Killer and History Heuristic
- Null Move Pruning

--
Srdja
User avatar
towforce
Posts: 12461
Joined: Thu Mar 09, 2006 12:57 am
Location: Birmingham UK
Full name: Graham Laight

Re: YATT - Yet Another Turing Test

Post by towforce »

Isn't expecting an LLM to produce a good hand-crafted evaluation a bit like expecting an LLM to play good chess - optimistic?

Happy to be proven wrong!
Human chess is partly about tactics and strategy, but mostly about memory
smatovic
Posts: 3297
Joined: Wed Mar 10, 2010 10:18 pm
Location: Hamburg, Germany
Full name: Srdja Matovic

Re: YATT - Yet Another Turing Test

Post by smatovic »

I find the second test candidate's evaluation function already astonishing for an LLM.

Will post further source code of next GPT/LLM versions in this thread when sighted. I am also curious how progress will be.

--
Srdja
User avatar
towforce
Posts: 12461
Joined: Thu Mar 09, 2006 12:57 am
Location: Birmingham UK
Full name: Graham Laight

Re: YATT - Yet Another Turing Test

Post by towforce »

smatovic wrote: Thu Jun 20, 2024 11:06 amI find the second test candidate's evaluation function already astonishing for an LLM.

I have had this experience with LLMs - in particular Gemini Pro (the paid version). I had it write a quest type story for a girl I know about her country, and she was astonished (rightly so - it was real quality!), and in a life coaching session yesterday, it came out with insights about me that I've never heard from a human. I copied it into my notes and added just one word - "WOW!". :)

I understand how Jerry Ehman felt in 1967 (link).

Obviously you cannot expect such experiences every day.
Human chess is partly about tactics and strategy, but mostly about memory
smatovic
Posts: 3297
Joined: Wed Mar 10, 2010 10:18 pm
Location: Hamburg, Germany
Full name: Srdja Matovic

Re: YATT - Yet Another Turing Test

Post by smatovic »

Third test candidate was posted by Werewolf, August 14, 2025, generated by GPT-5 Pro:

The Dawn of Skynet
viewtopic.php?t=85356

Code: Select all

/*
GPT-5 Pro Chess — a simple, fast(ish) brute-force UCI engine in C.

Design goals per user spec:
- UCI compatible; works in Fritz/ChessBase
- "Brute force" alpha-beta with iterative deepening + quiescence (captures; checks are handled by in-check extension)
- No exotic pruning like LMR/null-move/razoring. Basic move ordering only (TT move, MVV-LVA, killers, history).
- Simple evaluation: material + development + central control + mobility + king safety (non-linear attackers near king)
- Depth gauge + nodes per second (nps) reported via UCI "info" lines
- Hash option (MB) and Threads option (accepted; currently single-threaded by design for simplicity and robustness)

Build (MSYS2 UCRT64 GCC):
gcc -O3 -pipe -DNDEBUG -std=c17 -march=native -pthread -o gpt5prochess.exe gpt5prochess.c

This is a single-file engine to make it easy to drop into Cursor and build.
License: MIT
Author: OpenAI (GPT-5 Pro)
*/

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <inttypes.h>
#include <ctype.h>
#include <time.h>
#include <assert.h>
#include <stdbool.h>

#ifdef _WIN32
#include <windows.h>
#else
#include <sys/time.h>
#endif

/* ========================= Utility: time in milliseconds ========================= */
static inline uint64_t now_ms(void) {
#ifdef _WIN32
return (uint64_t)GetTickCount64();
#else
struct timeval tv;
gettimeofday(&tv, NULL);
return (uint64_t)tv.tv_sec * 1000ULL + (uint64_t)tv.tv_usec / 1000ULL;
#endif
}

/* ========================= 0x88 board representation ========================= */
#define SQ_OK(sq) (!((sq) & 0x88))
#define FILE_OF(sq) ((sq) & 7)
#define RANK_OF(sq) ((sq) >> 4)
#define SQ(f,r) (((r)<<4) | (f))

enum { WHITE = 0, BLACK = 1 };
enum { EMPTY=0,
WP=1, WN=2, WB=3, WR=4, WQ=5, WK=6,
BP=7, BN=8, BB=9, BR=10, BQ=11, BK=12 };

static inline int color_of(int p){ return p>=BP; }
static inline int type_of(int p){ if(p==EMPTY) return 0; return (p-1)%6 + 1; }

static const int piece_value[13] = {
0, 100, 320, 330, 500, 900, 20000, 100, 320, 330, 500, 900, 20000
};

/* Directions (0x88) */
static const int knight_dirs[8] = { 33, 31, 18, 14, -33, -31, -18, -14 };
static const int bishop_dirs[4] = { 15, 17, -15, -17 };
static const int rook_dirs[4] = { 16, 1, -16, -1 };
static const int king_dirs[8] = { 16, 1, -16, -1, 15, 17, -15, -17 };

/* Castling rights bitmask */
#define W_KS 1
#define W_QS 2
#define B_KS 4
#define B_QS 8

/* Move encoding: 32-bit
bits 0..6 from (0..127)
bits 7..13 to (0..127)
bits 14..17 promo piece type (1..6) (color inferred by side-to-move)
bits 18..23 flags
*/
typedef uint32_t Move;
#define MFROM(m) ((int)((m) & 0x7F))
#define MTO(m) ((int)(((m) >> 7) & 0x7F))
#define MPROM(m) ((int)(((m) >> 14) & 0xF))
#define MFLAGS(m) ((int)(((m) >> 18) & 0x3F))
static inline Move MOVE(int from, int to, int promo, int flags){
return (Move)( (from & 0x7F) | ((to & 0x7F)<<7) | ((promo & 0xF)<<14) | ((flags & 0x3F)<<18) );
}

/* Move flags */
#define FL_CAPTURE 1
#define FL_DPP 2 /* double pawn push */
#define FL_EPCAP 4
#define FL_CASTLE 8
#define FL_PROMO 16

/* Search constants */
#define MAX_PLY 128
#define INF 32000
#define MATE 30000
#define MATE_IN(ply) (MATE - (ply))

/* ========================= Engine state ========================= */
static int board[128];
static int side_to_move = WHITE;
static int castle_rights = W_KS|W_QS|B_KS|B_QS;
static int ep_square = -1; /* -1 = none, otherwise 0..127 */
static int halfmove_clock = 0;
static int fullmove_number = 1;
static int king_sq[2] = { SQ(4,0), SQ(4,7) };

/* For repetition detection */
static uint64_t hash_history[2048];
static int ply_history = 0;

/* ========================= Zobrist hashing ========================= */
static uint64_t zob_piece[13][128];
static uint64_t zob_side;
static uint64_t zob_castle[16];
static uint64_t zob_ep[16];
static uint64_t pos_key = 0;

static uint64_t xorshift64(uint64_t *s){
uint64_t x = *s;
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
*s = x;
return x;
}
static void init_zobrist(void){
uint64_t seed = 0x9E3779B97F4A7C15ULL; /* fixed seed */
for(int p=0;p<13;p++)
for(int sq=0;sq<128;sq++)
zob_piece[p][sq] = xorshift64(&seed);
zob_side = xorshift64(&seed);
for(int i=0;i<16;i++) zob_castle = xorshift64(&seed);
for(int i=0;i<16;i++) zob_ep = xorshift64(&seed);
}
static uint64_t compute_hash(void){
uint64_t h=0;
for(int sq=0;sq<128;sq++){
if(!SQ_OK(sq)) { sq = (sq|7); continue; }
int p = board[sq];
if(p) h ^= zob_piece[p][sq];
}
if(side_to_move==BLACK) h ^= zob_side;
h ^= zob_castle[castle_rights & 15];
if(ep_square!=-1) h ^= zob_ep[FILE_OF(ep_square)];
return h;
}

/* ========================= Random helpers ========================= */
static inline int sign(int x){ return (x>0)-(x<0); }

/* ========================= Move list ========================= */
typedef struct {
Move m;
int score;
} ScoredMove;

typedef struct {
ScoredMove list[256];
int count;
} MoveList;

static inline void ml_init(MoveList* ml){ ml->count = 0; }
static inline void ml_add(MoveList* ml, Move m, int score){
ml->list[ml->count].m = m;
ml->list[ml->count].score = score;
ml->count++;
}

/* ========================= Killer & History heuristics ========================= */
static Move killer_moves[2][MAX_PLY];
static int history_heur[2][128][128]; /* color, from, to */

static inline void history_clear(void){
memset(killer_moves, 0, sizeof(killer_moves));
memset(history_heur, 0, sizeof(history_heur));
}

/* ========================= TT (Transposition Table) ========================= */
typedef struct {
uint64_t key;
int16_t score;
int8_t depth;
uint8_t flag; /* 0=exact, 1=alpha, 2=beta */
uint32_t move;
} TTEntry;

static TTEntry *tt = NULL;
static size_t tt_size = 1<<20; /* entries */
static size_t tt_mask = (1<<20)-1;

static void tt_resize_mb(size_t mb){
if(tt) { free(tt); tt=NULL; }
size_t bytes = (size_t)mb * 1024ULL * 1024ULL;
size_t entries = bytes / sizeof(TTEntry);
if(entries < (1<<18)) entries = (1<<18); /* min size ~ 256k entries */
/* round down to power of two */
size_t pow2 = 1;
while((pow2<<1) <= entries) pow2 <<= 1;
tt_size = pow2;
tt_mask = tt_size - 1;
tt = (TTEntry*)malloc(tt_size * sizeof(TTEntry));
if(!tt){
fprintf(stderr, "Failed to allocate TT (%zu MB). Falling back to 16 MB.\n", mb);
tt_size = (1<<18);
tt_mask = tt_size - 1;
tt = (TTEntry*)malloc(tt_size * sizeof(TTEntry));
if(!tt){ fprintf(stderr, "Critical: cannot allocate TT.\n"); exit(1); }
}
memset(tt, 0, tt_size * sizeof(TTEntry));
}

static inline int score_to_tt(int score, int ply){
if(score > MATE_IN(100)) return score + ply; /* mate scores from root perspective */
if(score < -MATE_IN(100)) return score - ply;
return score;
}
static inline int score_from_tt(int score, int ply){
if(score > MATE_IN(100)) return score - ply;
if(score < -MATE_IN(100)) return score + ply;
return score;
}

static inline TTEntry* tt_probe(uint64_t key){
return &tt[key & tt_mask];
}
static inline void tt_store(uint64_t key, int depth, int flag, int score, Move m, int ply){
TTEntry* e = &tt[key & tt_mask];
if(e->key && e->depth > depth && e->key==key) return; /* keep deeper entry */
e->key = key;
e->depth = (int8_t) (depth>127?127:depth);
e->flag = (uint8_t)flag;
e->score = (int16_t)score_to_tt(score, ply);
e->move = m;
}

/* ========================= Position I/O ========================= */
static void set_startpos(void){
memset(board, 0, sizeof(board));
const int back_w[8] = { WR, WN, WB, WQ, WK, WB, WN, WR };
const int back_b[8] = { BR, BN, BB, BQ, BK, BB, BN, BR };
for(int f=0;f<8;f++){
board[SQ(f,0)] = back_w[f];
board[SQ(f,1)] = WP;
board[SQ(f,6)] = BP;
board[SQ(f,7)] = back_b[f];
}
side_to_move = WHITE;
castle_rights = W_KS|W_QS|B_KS|B_QS;
ep_square = -1;
halfmove_clock = 0;
fullmove_number = 1;
king_sq[WHITE] = SQ(4,0);
king_sq[BLACK] = SQ(4,7);
pos_key = compute_hash();
ply_history = 0;
hash_history[ply_history++] = pos_key;
}

static int parse_fen(const char* fen){
memset(board, 0, sizeof(board));
side_to_move = WHITE; castle_rights = 0; ep_square=-1; halfmove_clock=0; fullmove_number=1;
int f=0, r=7;
const char* p = fen;
while(*p && r>=0){
char c = *p++;
if(c==' '){ break; }
if(c=='/'){ f=0; r--; continue; }
if(isdigit((unsigned char)c)){ f += c - '0'; continue; }
int piece=0;
switch(c){
case 'P': piece=WP; break; case 'N': piece=WN; break; case 'B': piece=WB; break;
case 'R': piece=WR; break; case 'Q': piece=WQ; break; case 'K': piece=WK; break;
case 'p': piece=BP; break; case 'n': piece=BN; break; case 'b': piece=BB; break;
case 'r': piece=BR; break; case 'q': piece=BQ; break; case 'k': piece=BK; break;
default: return 0;
}
if(f>7||r<0) return 0;
board[SQ(f,r)] = piece;
if(piece==WK) king_sq[WHITE]=SQ(f,r);
if(piece==BK) king_sq[BLACK]=SQ(f,r);
f++;
}
/* side */
while(*p==' ') p++;
if(*p=='w') side_to_move=WHITE; else if(*p=='b') side_to_move=BLACK; else return 0;
while(*p && *p!=' ') p++;
if(*p==' ') p++;
/* castling */
castle_rights=0;
if(*p=='-') p++; else {
while(*p && *p!=' '){
if(*p=='K') castle_rights |= W_KS;
else if(*p=='Q') castle_rights |= W_QS;
else if(*p=='k') castle_rights |= B_KS;
else if(*p=='q') castle_rights |= B_QS;
else break;
p++;
}
}
while(*p==' ') p++;
/* ep */
if(*p=='-'){ ep_square=-1; p++; }
else{
if(p[0]>='a' && p[0]<='h' && p[1]>='1' && p[1]<='8'){
int file = p[0]-'a';
int rank = p[1]-'1';
ep_square = SQ(file, rank);
p+=2;
}else return 0;
}
while(*p==' ') p++;
/* halfmove */
if(isdigit((unsigned char)*p)){ halfmove_clock = atoi(p); while(*p && *p!=' ') p++; }
while(*p==' ') p++;
/* fullmove */
if(isdigit((unsigned char)*p)){ fullmove_number = atoi(p); }
pos_key = compute_hash();
ply_history = 0;
hash_history[ply_history++] = pos_key;
return 1;
}

/* ========================= Attack detection ========================= */
static bool square_attacked_by(int sq, int color){
/* pawns */
if(color==WHITE){
int s1 = sq - 15, s2 = sq - 17;
if(SQ_OK(s1) && board[s1]==WP) return true;
if(SQ_OK(s2) && board[s2]==WP) return true;
}else{
int s1 = sq + 15, s2 = sq + 17;
if(SQ_OK(s1) && board[s1]==BP) return true;
if(SQ_OK(s2) && board[s2]==BP) return true;
}
/* knights */
for(int i=0;i<8;i++){
int s = sq + knight_dirs;
if(!SQ_OK(s)) continue;
int p = board[s];
if(p && color_of(p)==color && type_of(p)==2) return true;
}
/* bishops/queens */
for(int d=0;d<4;d++){
int dir = bishop_dirs[d];
int s = sq + dir;
while(SQ_OK(s)){
int p = board[s];
if(p){
if(color_of(p)==color){
int t = type_of(p);
if(t==3 || t==5) return true;
}
break;
}
s += dir;
}
}
/* rooks/queens */
for(int d=0;d<4;d++){
int dir = rook_dirs[d];
int s = sq + dir;
while(SQ_OK(s)){
int p = board[s];
if(p){
if(color_of(p)==color){
int t = type_of(p);
if(t==4 || t==5) return true;
}
break;
}
s += dir;
}
}
/* king */
for(int i=0;i<8;i++){
int s = sq + king_dirs;
if(!SQ_OK(s)) continue;
int p = board[s];
if(p && color_of(p)==color && type_of(p)==6) return true;
}
return false;
}

static inline bool in_check(int color){
return square_attacked_by(king_sq[color], color^1);
}

/* ========================= Move generation (pseudo-legal) ========================= */
static inline int mvv_lva(int attacker, int victim){
/* capture ordering: victim value * 10 - attacker value (bigger first) */
return piece_value[type_of(victim)]*10 - piece_value[type_of(attacker)];
}

static void gen_moves_side(MoveList* ml, int side, bool captures_only){
ml_init(ml);
for(int sq=0;sq<128;sq++){
if(!SQ_OK(sq)){ sq = (sq|7); continue; }
int p = board[sq];
if(!p || color_of(p)!=side) continue;
int pt = type_of(p);
if(pt==1){ /* pawn */
if(side==WHITE){
int up = sq + 16;
if(!captures_only){
if(SQ_OK(up) && board[up]==EMPTY){
int toRank = RANK_OF(up);
if(toRank==7){
ml_add(ml, MOVE(sq,up,5,FL_PROMO), 0); /* promote to Q */
ml_add(ml, MOVE(sq,up,4,FL_PROMO), 0); /* R */
ml_add(ml, MOVE(sq,up,3,FL_PROMO), 0); /* B */
ml_add(ml, MOVE(sq,up,2,FL_PROMO), 0); /* N */
}else{
ml_add(ml, MOVE(sq,up,0,0), 0);
if(RANK_OF(sq)==1){
int up2 = sq + 32;
if(SQ_OK(up2) && board[up2]==EMPTY){
ml_add(ml, MOVE(sq,up2,0,FL_DPP), 0);
}
}
}
}
}
int capL = sq + 15, capR = sq + 17;
if(SQ_OK(capL)){
int v = board[capL];
if(v && color_of(v)!=side){
int promo = (RANK_OF(capL)==7)?5:0;
ml_add(ml, MOVE(sq,capL,promo,FL_CAPTURE | (promo?FL_PROMO:0)),
mvv_lva(p,v));
}else if(capL==ep_square){
ml_add(ml, MOVE(sq,capL,0,FL_EPCAP|FL_CAPTURE), 1050);
}
}
if(SQ_OK(capR)){
int v = board[capR];
if(v && color_of(v)!=side){
int promo = (RANK_OF(capR)==7)?5:0;
ml_add(ml, MOVE(sq,capR,promo,FL_CAPTURE | (promo?FL_PROMO:0)),
mvv_lva(p,v));
}else if(capR==ep_square){
ml_add(ml, MOVE(sq,capR,0,FL_EPCAP|FL_CAPTURE), 1050);
}
}
}else{ /* black */
int dn = sq - 16;
if(!captures_only){
if(SQ_OK(dn) && board[dn]==EMPTY){
int toRank = RANK_OF(dn);
if(toRank==0){
ml_add(ml, MOVE(sq,dn,5,FL_PROMO), 0);
ml_add(ml, MOVE(sq,dn,4,FL_PROMO), 0);
ml_add(ml, MOVE(sq,dn,3,FL_PROMO), 0);
ml_add(ml, MOVE(sq,dn,2,FL_PROMO), 0);
}else{
ml_add(ml, MOVE(sq,dn,0,0), 0);
if(RANK_OF(sq)==6){
int dn2 = sq - 32;
if(SQ_OK(dn2) && board[dn2]==EMPTY){
ml_add(ml, MOVE(sq,dn2,0,FL_DPP), 0);
}
}
}
}
}
int capL = sq - 17, capR = sq - 15;
if(SQ_OK(capL)){
int v = board[capL];
if(v && color_of(v)!=side){
int promo = (RANK_OF(capL)==0)?5:0;
ml_add(ml, MOVE(sq,capL,promo,FL_CAPTURE | (promo?FL_PROMO:0)),
mvv_lva(p,v));
}else if(capL==ep_square){
ml_add(ml, MOVE(sq,capL,0,FL_EPCAP|FL_CAPTURE), 1050);
}
}
if(SQ_OK(capR)){
int v = board[capR];
if(v && color_of(v)!=side){
int promo = (RANK_OF(capR)==0)?5:0;
ml_add(ml, MOVE(sq,capR,promo,FL_CAPTURE | (promo?FL_PROMO:0)),
mvv_lva(p,v));
}else if(capR==ep_square){
ml_add(ml, MOVE(sq,capR,0,FL_EPCAP|FL_CAPTURE), 1050);
}
}
}
}else if(pt==2){ /* knight */
for(int i=0;i<8;i++){
int to = sq + knight_dirs;
if(!SQ_OK(to)) continue;
int v = board[to];
if(v && color_of(v)==side) continue;
if(v) ml_add(ml, MOVE(sq,to,0,FL_CAPTURE), mvv_lva(p,v));
else if(!captures_only) ml_add(ml, MOVE(sq,to,0,0), 0);
}
}else if(pt==3 || pt==4 || pt==5){ /* bishop/rook/queen */
const int* dirs = (pt==3)?bishop_dirs : (pt==4?rook_dirs:king_dirs /* both sets */);
int dir_count = (pt==3 || pt==4) ? 4 : 8;
for(int d=0; d<dir_count; d++){
int to = sq + dirs[d];
while(SQ_OK(to)){
int v = board[to];
if(v){
if(color_of(v)!=side)
ml_add(ml, MOVE(sq,to,0,FL_CAPTURE), mvv_lva(p,v));
break;
}else{
if(!captures_only) ml_add(ml, MOVE(sq,to,0,0), 0);
}
to += dirs[d];
}
}
}else if(pt==6){ /* king */
for(int i=0;i<8;i++){
int to = sq + king_dirs;
if(!SQ_OK(to)) continue;
int v = board[to];
if(v && color_of(v)==side) continue;
if(v) ml_add(ml, MOVE(sq,to,0,FL_CAPTURE), mvv_lva(p,v));
else if(!captures_only) ml_add(ml, MOVE(sq,to,0,0), 0);
}
/* Castling */
if(!captures_only){
if(side==WHITE){
if((castle_rights & W_KS) &&
board[SQ(5,0)]==EMPTY && board[SQ(6,0)]==EMPTY &&
!square_attacked_by(SQ(4,0),BLACK) &&
!square_attacked_by(SQ(5,0),BLACK) &&
!square_attacked_by(SQ(6,0),BLACK)){
ml_add(ml, MOVE(SQ(4,0), SQ(6,0), 0, FL_CASTLE), 0);
}
if((castle_rights & W_QS) &&
board[SQ(3,0)]==EMPTY && board[SQ(2,0)]==EMPTY && board[SQ(1,0)]==EMPTY &&
!square_attacked_by(SQ(4,0),BLACK) &&
!square_attacked_by(SQ(3,0),BLACK) &&
!square_attacked_by(SQ(2,0),BLACK)){
ml_add(ml, MOVE(SQ(4,0), SQ(2,0), 0, FL_CASTLE), 0);
}
}else{
if((castle_rights & B_KS) &&
board[SQ(5,7)]==EMPTY && board[SQ(6,7)]==EMPTY &&
!square_attacked_by(SQ(4,7),WHITE) &&
!square_attacked_by(SQ(5,7),WHITE) &&
!square_attacked_by(SQ(6,7),WHITE)){
ml_add(ml, MOVE(SQ(4,7), SQ(6,7), 0, FL_CASTLE), 0);
}
if((castle_rights & B_QS) &&
board[SQ(3,7)]==EMPTY && board[SQ(2,7)]==EMPTY && board[SQ(1,7)]==EMPTY &&
!square_attacked_by(SQ(4,7),WHITE) &&
!square_attacked_by(SQ(3,7),WHITE) &&
!square_attacked_by(SQ(2,7),WHITE)){
ml_add(ml, MOVE(SQ(4,7), SQ(2,7), 0, FL_CASTLE), 0);
}
}
}
}
}
}

/* ========================= Make/Unmake ========================= */
typedef struct {
int castle, ep, halfmove;
uint64_t hash;
int captured;
int moved_piece;
int from, to;
int promo;
} Undo;

static Undo undo_stack[MAX_PLY];
static int sp = 0;

static inline void hash_toggle_piece(int p, int sq){ pos_key ^= zob_piece[p][sq]; }
static inline void hash_toggle_side(void){ pos_key ^= zob_side; }
static inline void hash_set_castle(int oldc, int newc){ pos_key ^= zob_castle[oldc & 15]; pos_key ^= zob_castle[newc & 15]; }
static inline void hash_set_ep(int old_ep, int new_ep){
if(old_ep!=-1) pos_key ^= zob_ep[FILE_OF(old_ep)];
if(new_ep!=-1) pos_key ^= zob_ep[FILE_OF(new_ep)];
}

static inline void remove_castle_rights_by_move(int piece, int from, int to){
int old = castle_rights, newc = castle_rights;
if(piece==WK){ newc &= ~(W_KS|W_QS); }
if(piece==BK){ newc &= ~(B_KS|B_QS); }
if(piece==WR){
if(from==SQ(0,0)) newc &= ~W_QS;
if(from==SQ(7,0)) newc &= ~W_KS;
}
if(piece==BR){
if(from==SQ(0,7)) newc &= ~B_QS;
if(from==SQ(7,7)) newc &= ~B_KS;
}
if(to==SQ(0,0)) newc &= ~W_QS;
if(to==SQ(7,0)) newc &= ~W_KS;
if(to==SQ(0,7)) newc &= ~B_QS;
if(to==SQ(7,7)) newc &= ~B_KS;
if(newc != old){ hash_set_castle(old, newc); castle_rights = newc; }
}

static int make_move(Move m){
int from = MFROM(m), to = MTO(m);
int flags = MFLAGS(m);
int promo = MPROM(m);
int us = side_to_move;
int them = us^1;
int piece = board[from];
int captured = 0;

Undo* u = &undo_stack[sp++];
u->castle = castle_rights;
u->ep = ep_square;
u->halfmove = halfmove_clock;
u->hash = pos_key;
u->captured = EMPTY;
u->moved_piece = piece;
u->from = from;
u->to = to;
u->promo = promo;

/* clear EP */
hash_set_ep(ep_square, -1);
ep_square = -1;

/* update halfmove */
if(type_of(piece)==1) halfmove_clock = 0; else halfmove_clock++;

/* hash piece move */
hash_toggle_piece(piece, from);
board[from] = EMPTY;

if(flags & FL_EPCAP){
int cap_sq = (us==WHITE) ? (to - 16) : (to + 16);
captured = board[cap_sq];
u->captured = captured;
board[cap_sq] = EMPTY;
hash_toggle_piece(captured, cap_sq);
halfmove_clock = 0;
} else if(board[to]!=EMPTY){
captured = board[to];
u->captured = captured;
hash_toggle_piece(captured, to);
halfmove_clock = 0;
}

if(flags & FL_CASTLE){
if(piece==WK && to==SQ(6,0)){
/* white short */
board[SQ(5,0)] = WR; board[SQ(7,0)] = EMPTY;
hash_toggle_piece(WR, SQ(5,0)); hash_toggle_piece(WR, SQ(7,0));
}else if(piece==WK && to==SQ(2,0)){
/* white long */
board[SQ(3,0)] = WR; board[SQ(0,0)] = EMPTY;
hash_toggle_piece(WR, SQ(3,0)); hash_toggle_piece(WR, SQ(0,0));
}else if(piece==BK && to==SQ(6,7)){
board[SQ(5,7)] = BR; board[SQ(7,7)] = EMPTY;
hash_toggle_piece(BR, SQ(5,7)); hash_toggle_piece(BR, SQ(7,7));
}else if(piece==BK && to==SQ(2,7)){
board[SQ(3,7)] = BR; board[SQ(0,7)] = EMPTY;
hash_toggle_piece(BR, SQ(3,7)); hash_toggle_piece(BR, SQ(0,7));
}
}

/* promotions */
if((flags & FL_PROMO) && promo){
int prom_piece = (us==WHITE) ? (promo) : (promo+6);
/* pawn has been removed from 'from' already */
board[to] = prom_piece;
hash_toggle_piece(prom_piece, to);
}else{
board[to] = piece;
hash_toggle_piece(piece, to);
}

/* update king sq */
if(type_of(piece)==6){
king_sq[us] = to;
}

/* set EP on double pawn push */
if(flags & FL_DPP){
ep_square = (us==WHITE) ? (to - 16) : (to + 16);
hash_set_ep(-1, ep_square);
}

/* update castle rights */
remove_castle_rights_by_move(piece, from, to);

/* side to move */
side_to_move ^= 1;
hash_toggle_side();

/* legality: did we leave our own king in check? */
if(in_check(them)){
/* illegal, undo */
side_to_move ^= 1;
hash_toggle_side();

/* undo piece restoration */
hash_toggle_piece(board[to], to);
board[to] = EMPTY;

if(flags & FL_EPCAP){
int cap_sq = (us==WHITE) ? (to - 16) : (to + 16);
board[from] = piece; hash_toggle_piece(piece, from);
board[cap_sq] = captured; hash_toggle_piece(captured, cap_sq);
}else{
board[from] = piece; hash_toggle_piece(piece, from);
if(captured){ board[to] = captured; hash_toggle_piece(captured, to); }
}
/* undo rook for castling */
if(flags & FL_CASTLE){
if(piece==WK && to==SQ(6,0)){
board[SQ(7,0)] = WR; hash_toggle_piece(WR, SQ(7,0));
board[SQ(5,0)] = EMPTY; hash_toggle_piece(WR, SQ(5,0));
}else if(piece==WK && to==SQ(2,0)){
board[SQ(0,0)] = WR; hash_toggle_piece(WR, SQ(0,0));
board[SQ(3,0)] = EMPTY; hash_toggle_piece(WR, SQ(3,0));
}else if(piece==BK && to==SQ(6,7)){
board[SQ(7,7)] = BR; hash_toggle_piece(BR, SQ(7,7));
board[SQ(5,7)] = EMPTY; hash_toggle_piece(BR, SQ(5,7));
}else if(piece==BK && to==SQ(2,7)){
board[SQ(0,7)] = BR; hash_toggle_piece(BR, SQ(0,7));
board[SQ(3,7)] = EMPTY; hash_toggle_piece(BR, SQ(3,7));
}
}
/* restore castle/ep/halfmove/hash */
hash_set_castle(castle_rights, u->castle);
castle_rights = u->castle;
hash_set_ep(ep_square, u->ep);
ep_square = u->ep;
halfmove_clock = u->halfmove;
pos_key = u->hash;
sp--;
return 0;
}

/* ok */
if(us==BLACK) fullmove_number++;
return 1;
}

static void unmake_move(void){
Undo* u = &undo_stack[--sp];
int from = u->from, to = u->to;
int piece_moved = u->moved_piece;
int captured = u->captured;
int flags = 0;
/* Recover flags from context if needed (not necessary for undo correctness) */

side_to_move ^= 1;
hash_toggle_side();

/* remove piece from 'to' */
int on_to = board[to];
hash_toggle_piece(on_to, to);
board[to] = EMPTY;

/* undo castling rook move if castle */
if(type_of(piece_moved)==6 && abs(FILE_OF(to)-FILE_OF(from))==2){
if(piece_moved==WK && to==SQ(6,0)){
board[SQ(7,0)] = WR; hash_toggle_piece(WR, SQ(7,0));
board[SQ(5,0)] = EMPTY; hash_toggle_piece(WR, SQ(5,0));
}else if(piece_moved==WK && to==SQ(2,0)){
board[SQ(0,0)] = WR; hash_toggle_piece(WR, SQ(0,0));
board[SQ(3,0)] = EMPTY; hash_toggle_piece(WR, SQ(3,0));
}else if(piece_moved==BK && to==SQ(6,7)){
board[SQ(7,7)] = BR; hash_toggle_piece(BR, SQ(7,7));
board[SQ(5,7)] = EMPTY; hash_toggle_piece(BR, SQ(5,7));
}else if(piece_moved==BK && to==SQ(2,7)){
board[SQ(0,7)] = BR; hash_toggle_piece(BR, SQ(0,7));
board[SQ(3,7)] = EMPTY; hash_toggle_piece(BR, SQ(3,7));
}
}

/* put back pawn if it was a promotion, else moved piece */
if(type_of(piece_moved)==1 && (RANK_OF(to)==7 || RANK_OF(to)==0) && type_of(on_to)!=1){
/* promotion was made; restore pawn at 'from' */
int pawn = (color_of(piece_moved)==WHITE)? WP : BP;
board[from] = pawn; hash_toggle_piece(pawn, from);
}else{
board[from] = piece_moved; hash_toggle_piece(piece_moved, from);
}
if(type_of(piece_moved)==6) king_sq[color_of(piece_moved)] = from;

/* restore captured */
if(u->captured){
if(type_of(u->captured)==1 && FILE_OF(to)!=FILE_OF(from) && board[to]==EMPTY){
/* was en-passant */
int cap_sq = (color_of(u->captured)==WHITE) ? (to - 16) : (to + 16);
board[cap_sq] = u->captured; hash_toggle_piece(u->captured, cap_sq);
}else{
board[to] = u->captured; hash_toggle_piece(u->captured, to);
}
}

/* restore state */
hash_set_castle(castle_rights, u->castle);
castle_rights = u->castle;
hash_set_ep(ep_square, u->ep);
ep_square = u->ep;
halfmove_clock = u->halfmove;
pos_key = u->hash;

if(side_to_move==BLACK) fullmove_number--;
}

/* ========================= Evaluation ========================= */
/* Simple evaluation:
- Material
- Development (penalty for undeveloped minor pieces early; bonus for castling)
- Central control (attacking d4/e4/d5/e5)
- Mobility (pseudo-legal mobility for non-pawns)
- King safety: attackers to king ring (non-linear)
*/

static int attackers_to_square_by(int sq, int side){
int count = 0;
/* pawns */
if(side==WHITE){
int s1 = sq - 15, s2 = sq - 17;
if(SQ_OK(s1) && board[s1]==WP) count++;
if(SQ_OK(s2) && board[s2]==WP) count++;
}else{
int s1 = sq + 15, s2 = sq + 17;
if(SQ_OK(s1) && board[s1]==BP) count++;
if(SQ_OK(s2) && board[s2]==BP) count++;
}
/* knights */
for(int i=0;i<8;i++){
int s = sq + knight_dirs;
if(!SQ_OK(s)) continue;
int p = board[s];
if(p && color_of(p)==side && type_of(p)==2) count++;
}
/* bishops/queens */
for(int d=0;d<4;d++){
int dir = bishop_dirs[d];
int s = sq + dir;
while(SQ_OK(s)){
int p = board[s];
if(p){
if(color_of(p)==side){
int t=type_of(p);
if(t==3||t==5){ count++; }
}
break;
}
s += dir;
}
}
/* rooks/queens */
for(int d=0;d<4;d++){
int dir = rook_dirs[d];
int s = sq + dir;
while(SQ_OK(s)){
int p = board[s];
if(p){
if(color_of(p)==side){
int t=type_of(p);
if(t==4||t==5){ count++; }
}
break;
}
s += dir;
}
}
/* king */
for(int i=0;i<8;i++){
int s = sq + king_dirs;
if(!SQ_OK(s)) continue;
int p = board[s];
if(p && color_of(p)==side && type_of(p)==6) count++;
}
return count;
}

static int mobility_side(int side){
int score=0;
for(int sq=0;sq<128;sq++){
if(!SQ_OK(sq)){ sq=(sq|7); continue; }
int p = board[sq];
if(!p || color_of(p)!=side) continue;
int pt = type_of(p);
if(pt==2){ /* knight */
for(int i=0;i<8;i++){
int to = sq + knight_dirs; if(!SQ_OK(to)) continue;
int v = board[to];
if(v==EMPTY || color_of(v)!=side) score+=2;
}
}else if(pt==3 || pt==4 || pt==5){
const int* dirs = (pt==3)?bishop_dirs:(pt==4?rook_dirs:king_dirs);
int dir_count = (pt==3 || pt==4)?4:8;
for(int d=0;d<dir_count;d++){
int to = sq + dirs[d];
while(SQ_OK(to)){
int v = board[to];
if(v){ if(color_of(v)!=side) score+=3; break; }
score+=1;
to += dirs[d];
}
}
}
}
return score;
}

static int eval(void){
int mat[2]={0,0};
for(int sq=0;sq<128;sq++){
if(!SQ_OK(sq)){ sq=(sq|7); continue; }
int p = board[sq];
if(p){
mat[color_of(p)] += piece_value[type_of(p)];
}
}
int score = (mat[WHITE] - mat[BLACK]);

/* Development penalties early (first ~16 plies from start). Use fullmove_number approx */
int undeveloped_w = 0, undeveloped_b=0;
if(fullmove_number <= 20){
if(board[SQ(1,0)]==WN) undeveloped_w += 8;
if(board[SQ(6,0)]==WN) undeveloped_w += 8;
if(board[SQ(2,0)]==WB) undeveloped_w += 8;
if(board[SQ(5,0)]==WB) undeveloped_w += 8;
if(board[SQ(1,7)]==BN) undeveloped_b += 8;
if(board[SQ(6,7)]==BN) undeveloped_b += 8;
if(board[SQ(2,7)]==BB) undeveloped_b += 8;
if(board[SQ(5,7)]==BB) undeveloped_b += 8;
/* bonus for castled king */
if(king_sq[WHITE]==SQ(6,0) || king_sq[WHITE]==SQ(2,0)) score += 12;
if(king_sq[BLACK]==SQ(6,7) || king_sq[BLACK]==SQ(2,7)) score -= 12;
}
score -= undeveloped_w;
score += undeveloped_b;

/* Central control (d4,e4,d5,e5 attacks) */
int center_sqs[4] = { SQ(3,3), SQ(4,3), SQ(3,4), SQ(4,4) };
int ctrl_w=0, ctrl_b=0;
for(int i=0;i<4;i++){
ctrl_w += attackers_to_square_by(center_sqs, WHITE) ? 1 : 0;
ctrl_b += attackers_to_square_by(center_sqs[i], BLACK) ? 1 : 0;
}
score += (ctrl_w - ctrl_b)*8;

/* Mobility */
int mob_w = mobility_side(WHITE);
int mob_b = mobility_side(BLACK);
score += (mob_w - mob_b);

/* King safety (non-linear attackers to ring) */
int ring_w[8]; int rwc=0;
int ring_b[8]; int rbc=0;
/* Build ring squares around each king */
for(int i=0;i<8;i++){
int s = king_dirs[i];
int sw = king_sq[WHITE] + s;
if(SQ_OK(sw)) ring_w[rwc++] = sw;
int sb = king_sq[BLACK] + s;
if(SQ_OK(sb)) ring_b[rbc++] = sb;
}
int atk_w=0, atk_b=0;
for(int i=0;i<rbc;i++) if(attackers_to_square_by(ring_b[i], WHITE)) atk_w++;
for(int i=0;i<rwc;i++) if(attackers_to_square_by(ring_w[i], BLACK)) atk_b++;
score += (atk_w*atk_w*8) - (atk_b*atk_b*8);

/* Side to move perspective */
return (side_to_move==WHITE)? score : -score;
}

/* ========================= Quiescence ========================= */
static uint64_t nodes = 0;
static uint64_t start_time_ms = 0;
static uint64_t stop_time_ms = 0;
static int stop_search = 0;

static inline void check_time(void){
if(stop_time_ms && (now_ms() >= stop_time_ms)) stop_search = 1;
}

static int quiescence(int alpha, int beta, int ply){
if(stop_search) return alpha;
nodes++;

int stand = eval();
if(stand >= beta) return beta;
if(alpha < stand) alpha = stand;

/* If in check, we must generate full moves to resolve it */
if(in_check(side_to_move)){
MoveList ml; gen_moves_side(&ml, side_to_move, false);
for(int i=0;i<ml.count;i++){
Move m = ml.list[i].m;
if(!make_move(m)) continue;
int score = -quiescence(-beta, -alpha, ply+1);
unmake_move();
if(stop_search) return alpha;
if(score >= beta) return beta;
if(score > alpha) alpha = score;
}
return alpha;
}

/* normal quiescence: only captures and ep captures */
MoveList ml; gen_moves_side(&ml, side_to_move, true);
/* order captures by MVV-LVA score (already set) — do a simple insertion sort */
for(int i=1;i<ml.count;i++){
ScoredMove key = ml.list[i];
int j=i-1;
while(j>=0 && ml.list[j].score < key.score){
ml.list[j+1] = ml.list[j]; j--;
}
ml.list[j+1] = key;
}
for(int i=0;i<ml.count;i++){
Move m = ml.list[i].m;
if(!(MFLAGS(m) & FL_CAPTURE)) continue; /* ensure capture */
if(!make_move(m)) continue;
int score = -quiescence(-beta, -alpha, ply+1);
unmake_move();
if(stop_search) return alpha;
if(score >= beta) return beta;
if(score > alpha) alpha = score;
}
return alpha;
}

/* ========================= PV handling ========================= */
static Move pv_table[MAX_PLY][MAX_PLY];
static int pv_len[MAX_PLY];

static inline void pv_clear(void){
for(int i=0;i<MAX_PLY;i++){ pv_len[i]=0; }
}

/* ========================= Search (alpha-beta) ========================= */
static int search(int depth, int alpha, int beta, int ply){
pv_len[ply] = 0;
if(stop_search){ return alpha; }
if(ply >= MAX_PLY-1) return eval();

nodes++;
if((nodes & 2047ULL)==0) check_time();
if(stop_search) return alpha;

int is_in_check = in_check(side_to_move);
if(is_in_check) depth++; /* check extension */

/* draw checks */
if(halfmove_clock >= 100){
return 0;
}
/* repetition: check earlier positions every 2 plies back until last capture/pawn move */
for(int i=ply_history-2; i>=0 && i>ply_history - 2*halfmove_clock - 2; i-=2){
if(hash_history[i]==pos_key) return 0;
}

if(depth <= 0){
return quiescence(alpha, beta, ply);
}

/* Transposition table probe */
Move tt_move = 0;
TTEntry* e = tt_probe(pos_key);
if(e->key == pos_key && e->depth >= depth){
int tt_score = score_from_tt(e->score, ply);
if(e->flag==0) return tt_score;
else if(e->flag==1 && tt_score <= alpha) return alpha;
else if(e->flag==2 && tt_score >= beta) return beta;
tt_move = (Move)e->move;
}

MoveList ml; gen_moves_side(&ml, side_to_move, false);

/* simple move ordering: TT move first, then captures by MVV-LVA, then killers, then history */
/* score moves */
for(int i=0;i<ml.count;i++){
Move m = ml.list[i].m;
int score = 0;
if(m==tt_move) score = 1<<30;
else if(MFLAGS(m)&FL_CAPTURE){
int from = MFROM(m), to = MTO(m);
int attacker = board[from];
int victim;
int capflag = MFLAGS(m)&FL_EPCAP;
if(capflag){
victim = (side_to_move==WHITE)? BP : WP;
}else{
victim = board[to];
}
score = 1<<20 + mvv_lva(attacker, victim);
}else{
if(killer_moves[0][ply]==m) score = (1<<19);
else if(killer_moves[1][ply]==m) score = (1<<19)-1;
else score = history_heur[side_to_move][MFROM(m)][MTO(m)] & ((1<<18)-1);
}
ml.list[i].score = score;
}
/* sort (insertion sort) */
for(int i=1;i<ml.count;i++){
ScoredMove key = ml.list[i];
int j=i-1;
while(j>=0 && ml.list[j].score < key.score){
ml.list[j+1] = ml.list[j]; j--;
}
ml.list[j+1] = key;
}

int legal = 0;
Move best_move = 0;
int best_score = -INF;
int orig_alpha = alpha;

for(int i=0;i<ml.count;i++){
Move m = ml.list[i].m;
if(!make_move(m)) continue;
hash_history[ply_history++] = pos_key;
int score = -search(depth-1, -beta, -alpha, ply+1);
ply_history--;
unmake_move();
if(stop_search) return alpha;

if(score > best_score){
best_score = score;
best_move = m;
if(score > alpha){
alpha = score;
/* update PV: prefix best child PV with this move */
pv_table[ply][0] = m;
for(int j=0; j<pv_len[ply+1]; j++) pv_table[ply][j+1] = pv_table[ply+1][j];
pv_len[ply] = pv_len[ply+1] + 1;
}
if(alpha >= beta){
/* update killers/history for quiet moves */
if(!(MFLAGS(m)&FL_CAPTURE)){
if(killer_moves[0][ply] != m){
killer_moves[1][ply] = killer_moves[0][ply];
killer_moves[0][ply] = m;
}
history_heur[side_to_move][MFROM(m)][MTO(m)] += depth*depth;
}
break;
}
}
legal++;
}

if(legal==0){
if(is_in_check) return -MATE_IN(ply);
return 0; /* stalemate */
}

/* store TT */
int flag = 0;
if(best_score <= orig_alpha) flag=1;
else if(best_score >= beta) flag=2;
else flag=0;
tt_store(pos_key, depth, flag, best_score, best_move, ply);

return best_score;
}

/* ========================= UCI helpers ========================= */
static const char* startpos_fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";

static void move_to_str(Move m, char out[8]){
int from = MFROM(m), to = MTO(m);
out[0] = 'a' + FILE_OF(from);
out[1] = '1' + RANK_OF(from);
out[2] = 'a' + FILE_OF(to);
out[3] = '1' + RANK_OF(to);
int promo = MPROM(m);
if(promo){
char pc='q';
if(promo==2) pc='n'; else if(promo==3) pc='b'; else if(promo==4) pc='r'; else pc='q';
/* adjust case for side (always lowercase for UCI) */
out[4] = pc;
out[5] = 0;
}else{
out[4] = 0;
}
}

static Move parse_move_uci(const char* s){
int len = (int)strlen(s);
if(len < 4) return 0;
int ff = s[0]-'a', fr = s[1]-'1';
int tf = s[2]-'a', tr = s[3]-'1';
if(ff<0||ff>7||tf<0||tf>7||fr<0||fr>7||tr<0||tr>7) return 0;
int from = SQ(ff,fr), to = SQ(tf,tr);
int want_promo=0;
if(len>=5){
char c = tolower((unsigned char)s[4]);
if(c=='q') want_promo=5;
else if(c=='r') want_promo=4;
else if(c=='b') want_promo=3;
else if(c=='n') want_promo=2;
}

MoveList ml; gen_moves_side(&ml, side_to_move, false);
/* find matching legal move */
for(int i=0;i<ml.count;i++){
Move m = ml.list[i].m;
if(MFROM(m)==from && MTO(m)==to){
if(MPROM(m) && want_promo && MPROM(m)!=want_promo) continue;
if(make_move(m)){
unmake_move();
return m;
}
}
}
return 0;
}

/* ========================= Iterative deepening & time control ========================= */
typedef struct {
int use_time; /* boolean */
int depth;
uint64_t movetime_ms;
uint64_t wtime, btime, winc, binc;
int movestogo;
} GoParams;

static void uci_info_line(int depth, int score, Move best, int pvlen){
uint64_t now = now_ms();
uint64_t elapsed = (now - start_time_ms);
if(elapsed==0) elapsed=1;
uint64_t nps = (nodes * 1000ULL) / elapsed;
char bestStr[8]; move_to_str(best, bestStr);
/* Build PV string */
char pvbuf[4096]; pvbuf[0]=0;
for(int i=0;i<pvlen && i<MAX_PLY;i++){
char s[8]; move_to_str(pv_table[0][i], s);
if(i) strcat(pvbuf, " ");
strcat(pvbuf, s);
}
if(abs(score) > MATE - 1000){
int mate_in = (score>0) ? (MATE - score + 1)/2 : - (MATE + score + 1)/2;
printf("info depth %d score mate %d time %" PRIu64 " nodes %" PRIu64 " nps %" PRIu64 " pv %s\n",
depth, mate_in, elapsed, nodes, nps, pvbuf);
}else{
printf("info depth %d score cp %d time %" PRIu64 " nodes %" PRIu64 " nps %" PRIu64 " pv %s\n",
depth, score, elapsed, nodes, nps, pvbuf);
}
fflush(stdout);
}

static uint64_t compute_move_budget_ms(const GoParams* gp){
if(gp->movetime_ms) return gp->movetime_ms;
if(gp->wtime==0 && gp->btime==0) return 0; /* no time control => search to depth only */
uint64_t mytime = (side_to_move==WHITE) ? gp->wtime : gp->btime;
uint64_t myinc = (side_to_move==WHITE) ? gp->winc : gp->binc;
int mtg = gp->movestogo ? gp->movestogo : 30;
uint64_t budget = mytime / (mtg+2) + myinc/2;
if(budget > mytime/3) budget = mytime/3;
if(budget < 10) budget = 10;
return budget;
}

static Move search_root(const GoParams* gp){
nodes = 0;
stop_search = 0;
pv_clear();
history_clear();

uint64_t budget = compute_move_budget_ms(gp);
start_time_ms = now_ms();
stop_time_ms = budget ? (start_time_ms + budget) : 0;

Move bestmove = 0;
int bestscore = -INF;

MoveList root; gen_moves_side(&root, side_to_move, false);
/* Ensure legal */
MoveList root_legal; ml_init(&root_legal);
for(int i=0;i<root.count;i++){
if(make_move(root.list[i].m)){
unmake_move();
ml_add(&root_legal, root.list[i].m, 0);
}
}

if(root_legal.count==0){
/* no moves */
return 0;
}

for(int depth=1; depth <= (gp->depth?gp->depth:64); depth++){
int alpha = -INF, beta = INF;
bestscore = -INF;
bestmove = root_legal.list[0].m;
pv_len[0]=0;

for(int i=0;i<root_legal.count;i++){
Move m = root_legal.list[i].m;
if(!make_move(m)) continue;
hash_history[ply_history++] = pos_key;
int score = -search(depth-1, -beta, -alpha, 1);
ply_history--;
unmake_move();
if(stop_search) break;
if(score > bestscore){
bestscore = score;
bestmove = m;
alpha = score;
/* build PV from child */
pv_table[0][0] = m;
for(int j=0; j<pv_len[1]; j++) pv_table[0][j+1] = pv_table[1][j];
pv_len[0] = pv_len[1] + 1;
}
}
if(stop_search) break;
uci_info_line(depth, bestscore, bestmove, pv_len[0]);
/* aspiration windows can be added later; keep brute force flavour */
}

return bestmove;
}

/* ========================= UCI loop ========================= */
static void uci_loop(void){
char line[4096];
printf("id name GPT-5 Pro Chess\n");
printf("id author OpenAI (GPT-5 Pro)\n");
printf("option name Hash type spin default 64 min 16 max 65536\n");
printf("option name Threads type spin default 1 min 1 max 32\n");
printf("uciok\n");
fflush(stdout);

GoParams gp = {0};
gp.depth = 0; gp.movetime_ms=0; gp.wtime=gp.btime=gp.winc=gp.binc=0; gp.movestogo=0;

while(fgets(line, sizeof(line), stdin)){
if(strncmp(line,"uci",3)==0){
printf("id name GPT-5 Pro Chess\n");
printf("id author OpenAI (GPT-5 Pro)\n");
printf("option name Hash type spin default 64 min 16 max 65536\n");
printf("option name Threads type spin default 1 min 1 max 32\n");
printf("uciok\n");
fflush(stdout);
}else if(strncmp(line,"isready",7)==0){
printf("readyok\n"); fflush(stdout);
}else if(strncmp(line,"ucinewgame",10)==0){
set_startpos();
if(tt) memset(tt,0,tt_size*sizeof(TTEntry));
}else if(strncmp(line,"position",8)==0){
char* p = line + 8;
while(*p==' ') p++;
if(strncmp(p,"startpos",8)==0){
set_startpos();
p += 8;
}else if(strncmp(p,"fen",3)==0){
p+=3; while(*p==' ') p++;
char fenbuf[256]; int idx=0;
/* read fen up to " moves" or end-of-line */
while(*p && *p!='\n'){
if(strncmp(p," moves",6)==0) break;
if(idx<255) fenbuf[idx++] = *p;
p++;
}
fenbuf[idx]=0;
if(!parse_fen(fenbuf)){
/* fallback */
set_startpos();
}
}else{
/* default to startpos */
set_startpos();
}
/* moves list */
char* movespos = strstr(line, "moves");
if(movespos){
movespos += 5;
while(*movespos){
while(*movespos==' ') movespos++;
if(!*movespos || *movespos=='\n') break;
char mv[8]={0};
int i=0;
while(*movespos && *movespos!=' ' && *movespos!='\n' && i<7){
mv[i++] = *movespos++;
}
mv[i]=0;
if(i>=4){
Move m = parse_move_uci(mv);
if(m) { make_move(m); hash_history[ply_history++] = pos_key; }
}
}
}
}else if(strncmp(line,"setoption",9)==0){
/* setoption name X value Y */
char* p = strstr(line, "name");
if(!p) continue;
p+=4; while(*p==' ') p++;
char name[64]={0}, value[64]={0};
int ni=0, vi=0;
while(*p && *p!=' ' && *p!='\n'){ if(ni<63) name[ni++]=*p; p++; }
char* v = strstr(line, "value");
if(v){ v+=5; while(*v==' ') v++; while(*v && *v!='\n'){ if(vi<63) value[vi++]=*v; v++; } }
name[ni]=0; value[vi]=0;
if(strcmp(name,"Hash")==0){
int mb = atoi(value);
if(mb<16) mb=16; if(mb>65536) mb=65536;
tt_resize_mb((size_t)mb);
}else if(strcmp(name,"Threads")==0){
/* present but not used (single-threaded) */
}
}else if(strncmp(line,"go",2)==0){
gp.depth = 0; gp.movetime_ms=0; gp.wtime=gp.btime=gp.winc=gp.binc=0; gp.movestogo=0;
char* p = line + 2;
while(*p){
while(*p==' ') p++;
if(!*p) break;
if(strncmp(p,"wtime",5)==0){ p+=5; gp.wtime = strtoull(p,&p,10); }
else if(strncmp(p,"btime",5)==0){ p+=5; gp.btime = strtoull(p,&p,10); }
else if(strncmp(p,"winc",4)==0){ p+=4; gp.winc = strtoull(p,&p,10); }
else if(strncmp(p,"binc",4)==0){ p+=4; gp.binc = strtoull(p,&p,10); }
else if(strncmp(p,"movestogo",9)==0){ p+=9; gp.movestogo = (int)strtoul(p,&p,10); }
else if(strncmp(p,"movetime",8)==0){ p+=8; gp.movetime_ms = strtoull(p,&p,10); }
else if(strncmp(p,"depth",5)==0){ p+=5; gp.depth = (int)strtoul(p,&p,10); }
else if(strncmp(p,"infinite",8)==0){ p+=8; gp.movetime_ms = 0; }
else { p++; }
}
Move bm = search_root(&gp);
char s[8]={0};
if(bm){ move_to_str(bm, s); printf("bestmove %s\n", s); }
else { printf("bestmove 0000\n"); }
fflush(stdout);
}else if(strncmp(line,"stop",4)==0){
stop_search = 1;
}else if(strncmp(line,"quit",4)==0){
break;
}else if(strncmp(line,"print",5)==0){
/* debug: print board */
for(int r=7;r>=0;r--){
for(int f=0;f<8;f++){
int p = board[SQ(f,r)];
char c = '.';
switch(p){
case WP:c='P';break; case WN:c='N';break; case WB:c='B';break; case WR:c='R';break; case WQ:c='Q';break; case WK:c='K';break;
case BP:c='p';break; case BN:c='n';break; case BB:c='b';break; case BR:c='r';break; case BQ:c='q';break; case BK:c='k';break;
default: c='.';
}
printf("%c ", c);
}
printf("\n");
}
fflush(stdout);
}
}
}

/* ========================= main ========================= */
int main(void){
init_zobrist();
tt_resize_mb(64); /* default */
set_startpos();
uci_loop();
if(tt) free(tt);
return 0;
}
*code formatting was lost during posting

- C17 source code
- UCI protocol
- 0x88 board representation
- 0x88 pseudo legal move generator
- AlphaBeta search as negamax
- move ordering with MVV-LVA
- Quiescence Search with Stand Pat
- check extension
- repetition detection
- Iterative Deepening Framework
- single thread search
- time control
- eval with material + development + central control + mobility + king safety
- Zobrist hashing
- Transposition Tables
- Killer and History Heuristic
- user prompt asked for no selective search beside move ordering

--
Srdja
User avatar
hgm
Posts: 28361
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Re: YATT - Yet Another Turing Test

Post by hgm »

smatovic wrote: Wed Jun 19, 2024 8:21 amThe Chinese Room Argument applied onto this test would claim that there is no conscious in need to perform such a task, hence this test is not meant to measure self-awareness, consciousness or sentience, but what we call human intelligence.
The Chinese Room Argument has always struck me as utter bullshit, at a level that even an idiot should be able to recognize as such. So it baffles me that it has even acquired a mention in serious AI discussion.

It should be obvious that nothing can act as if it understand something without actually understanding it. Unless you somehow subvert the meaning of 'understanding'. It should also be obvious that when the Room would pass the Turing test, it not necessarily implies that the processor (be it a human or computer CPU) must be responsible for this. It is of course the rule book that 'understands' Chinese.

Of course a simple, static rule book without some working memory that keeps track of some model of the outside world as well as previous events within the Room (such as the original Eliza program) stands no chance at all to pass the Turing test. At best it would simulate a completely demented person, and in my experience this becomes apparent within a minute when you try to have a conversation. By the time you have designed a rule book for updating the Room's memory as well as providing output that makes the Room able to pass the test, it would be most likely also possible to explicitly identfy the 'understanding' as the appropriate state change of the Room's memory.

As for an AI writing a chess engine: since chess engines are likely to be in the data with which it is trained, can you really say it 'creates' one? Has a C++ compiler that compiles Stockfish created an engine written in machine code? And when you decompile the machine code to some other language? What does 'outperforming' mean here? Must the resulting engine be stronger than Stockfish?
chrisw
Posts: 4631
Joined: Tue Apr 03, 2012 4:28 pm
Location: Midi-Pyrénées
Full name: Christopher Whittington

Re: YATT - Yet Another Turing Test

Post by chrisw »

hgm wrote: Mon Aug 18, 2025 1:24 pm
smatovic wrote: Wed Jun 19, 2024 8:21 amThe Chinese Room Argument applied onto this test would claim that there is no conscious in need to perform such a task, hence this test is not meant to measure self-awareness, consciousness or sentience, but what we call human intelligence.
The Chinese Room Argument has always struck me as utter bullshit, at a level that even an idiot should be able to recognize as such. So it baffles me that it has even acquired a mention in serious AI discussion.

It should be obvious that nothing can act as if it understand something without actually understanding it. Unless you somehow subvert the meaning of 'understanding'. It should also be obvious that when the Room would pass the Turing test, it not necessarily implies that the processor (be it a human or computer CPU) must be responsible for this. It is of course the rule book that 'understands' Chinese.

Of course a simple, static rule book without some working memory that keeps track of some model of the outside world as well as previous events within the Room (such as the original Eliza program) stands no chance at all to pass the Turing test. At best it would simulate a completely demented person, and in my experience this becomes apparent within a minute when you try to have a conversation. By the time you have designed a rule book for updating the Room's memory as well as providing output that makes the Room able to pass the test, it would be most likely also possible to explicitly identfy the 'understanding' as the appropriate state change of the Room's memory.

As for an AI writing a chess engine: since chess engines are likely to be in the data with which it is trained, can you really say it 'creates' one? Has a C++ compiler that compiles Stockfish created an engine written in machine code? And when you decompile the machine code to some other language? What does 'outperforming' mean here? Must the resulting engine be stronger than Stockfish?
Every day one comes across people who have been trained on MSM, Hollywood and general BS propaganda lies who confidently re-gurgitate the talking points they got trained on since birth. It’s all utterly incoherent, often psychopathic, lacking empathy, but they all pass the Turing Test, we know they are human idiots. Whereas Grok, Gemini et al, trained on the entire range of available text ever written (including of course forbidden texts such as Marx or Freud or modern philosophers) produce (so long as no human hand has tried to add or subtract what it considers bias) generate extremely smart and coherent responses. One can detect immediately, for example the ability to see all sides of everything, and realise this has to be AI since very few humans have equivalent capability. Thus they fail the Turing Test.

Not to say of course that the AI isn’t just regurgitating, but the wide input range and inclusion of forbidden texts make it exceptionally powerful. Could be with ability to internally cross question inconsistencies, paradoxes and so on, AI will/can rise way above human BS and lies.
smatovic
Posts: 3297
Joined: Wed Mar 10, 2010 10:18 pm
Location: Hamburg, Germany
Full name: Srdja Matovic

Re: YATT - Yet Another Turing Test

Post by smatovic »

hgm wrote: Mon Aug 18, 2025 1:24 pm The Chinese Room Argument has always struck me as utter bullshit,
[...]
I myself am ambivalent in regard of the Chinese Room Argument, maybe it raises more questions than it answers, and you can counter it by calling humans philosophical zombies:

https://en.wikipedia.org/wiki/Philosophical_zombie
hgm wrote: Mon Aug 18, 2025 1:24 pm [...]
As for an AI writing a chess engine: since chess engines are likely to be in the data with which it is trained, can you really say it 'creates' one? Has a C++ compiler that compiles Stockfish created an engine written in machine code? And when you decompile the machine code to some other language?
[...]
In my original post I wrote to "code" a chess engine, not to "create". Idk if generative AI can be called creative. What is the difference of an AI generating new images by mixing different inputs into something new and a human creative mind? Idk. A human mind creating something new by exploring the unknown, can a machine do this? Emergence? Or is everything pre-determined? Idk. But take a look into Jost Triller's Stockfish patch experiment, code and reasoning:

Re: ChatGPT usage in computer chess?
viewtopic.php?p=981757#p981757

hgm wrote: Mon Aug 18, 2025 1:24 pm [...]
What does 'outperforming' mean here? Must the resulting engine be stronger than Stockfish?
Yes, this one we can measure, speed, search depth, playing strength in Elo.
smatovic wrote: Wed Jun 19, 2024 8:21 am [...]
An AI which is able to code a chess engine and outperforms humans in this task.
[...]
2A) Outperforms non-programmers. 2B) Outperforms average chess-programmers. 2C) Outperforms top chess-programmers.
[...]
The first test candidate reached the level of 2A) now the third test candidate enters the level of 2B).

To reach top-engine level the AI will probably need a test framework feedback loop - update mental model, ponder, come up with ideas, implement idea, test idea (with ~10K games in self-play for 10 Elo steps and ~100K games for 1 Elo steps), commit or reject idea, repeat.

Someone said once, "Intelligence is how you perform in an given environment.". By defining the environment and certain goals we can measure intelligence.

--
Srdja