NICE Chess Engine ♟️

Overview

The Not Intelligent Chess Engine, otherwise known as NICE, is a UCI chess engine that implements key learning principles using low-level languages. NICE is developed in C++ for speed and efficiency, allowing us to utilize low-level operations and CPU instructions to further speed up the engine. The engine uses Data-Oriented Design principles, prioritizing performance over object-oriented data representation.

NICE is live on Lichess for all to try, challenge, and test its strength. It is comparable to Stockfish Level 5, with an estimated Elo rating of 1500-1700.

🎮 Play Against NICE on Lichess

Technical Highlights


Board Representation

There are typically 2 ways to represent they game board in an engine: Mailbox and Bitboards.

Approach 1: Mailbox

Mailbox is the more intuitive approach where the board is represented as an array of 64 elements where each element contains the ID of what type of piece is on that square.

Advantages ✅

Disadvantages ❌


Approach 2: Bitboards

Bitboard is a way of compressing the idea of the mailbox into a single 64-bit integer. It leverages the fact that there are 64 squares on the board and that modern computer hardware and CPUs have a 64-bit architecture. This means we can fit information of an entire board inside a single CPU register.

Why This Matters

Each bit in the 64-bit integer represents one square on the board. If the bit is 1, there’s a piece on that square. If it’s 0, the square is empty.

56 57 58 59 60 61 62 63
48 49 50 51 52 53 54 55
40 41 42 43 44 45 46 47
32 33 34 35 36 37 38 39
24 25 26 27 28 29 30 31
16 17 18 19 20 21 22 23
8 9 10 11 12 13 14 15
0 1 2 3 4 5 6 7

If we visualise the board being numbered as show above, we can map each square to 1 bit in a 64 bit unsigned integer.

56 57 58 59 60 61 62 63
48 49 50 51 52 53 54 55
40 41 42 43 44 45 46 47
32 33 34 35 36 37 38 39
24 25 26 27 28 29 30 31
16 17 18 19 20 21 22 23
0 1 2 3 4 5 6 7

The board above with pawns can be represented internally as:

0b00000000 00000000 00000000 00000000 00000000 00000000 11111111 00000000

In this example, bits 8 to 15 have been flipped to 1, representing pieces occupying those squares.

Advantages ✅

Disadvantages ❌


The Solution: Hybrid Approach 🎯

The hybrid approach combines both Bitboards and Mailbox. Notice how the strengths of one representation perfectly complement the weaknesses of the other!

How NICE Uses Both

The Trade-off

The only overhead is ensuring both representations stay synchronized to prevent board corruption. This is a small price to pay for the combined benefits of both approaches!


Move Generation

Move Encoding

A move is represented as a compact 32-bit integer for maximum efficiency and minimal memory footprint.

Bit Layout

Bits Purpose Range
0-5 From square 0-63
6-11 To square 0-63
12-17 Flags Castling, en passant, captures
18-21 Promoted to Piece type
22-25 Piece captured Piece type

This compact representation means each move takes just 4 bytes of memory!


Movement Categories

Chess pieces fall into two fundamental categories based on how they move:

🔹 Sliders

Pieces that slide across the board until hitting another piece or the edge

🔸 Leapers

Pieces that “teleport” to their destination without blockers affecting them


Slider Move Generation

Using directional offsets, we calculate the next position iteratively. The process loops until:

The offsets act as cardinal directions for movement:

+7 +9
-9 -7
+8
-1 +1
-8

Leaper Move Generation

Since a Knight at square E4 always attacks the same squares, we don’t need runtime calculations!

Optimization Strategy

  1. Pre-compute lookup tables for all 64 squares at startup
  2. Single array lookup at runtime: Attacks = KnightTable[square_index]
  3. Same approach works for Kings

This turns move generation into an O(1) operation!

X X
X X
X X
X X

Special Case: Pawns ♙

Pawns are unique hybrids:

We handle them with specific bitmasks accounting for:


Board Evaluation

Material Evaluation

Board evaluation is primarily based on material value - a simple but highly effective approach.

Benefits


Positional Evaluation

Material alone creates a tactical genius with no strategic sense. Without positional awareness, the engine might place knights on terrible squares!

Solution: Piece-Square Tables (PST)

PSTs assign positional bonuses for placing pieces on optimal squares.

Example: Knight PST

-50 -40 -30 -30 -30 -30 -40 -50
-40 -20 0 0 0 0 -20 -40
-30 0 10 15 15 10 0 -30
-30 5 15 20 20 15 5 -30
-30 0 15 20 20 15 0 -30
-30 5 10 15 15 10 5 -30
-40 -20 0 5 5 0 -20 -40
-50 -40 -30 -30 -30 -30 -40 -50

Key Insights from Knight PST

This teaches the engine positional chess principles through numbers!


Search Algorithm

Negamax with Alpha-Beta Pruning

NICE implements Negamax, an elegant optimization of the Minimax algorithm based on a simple principle:

What’s good for you is equally bad for your opponent

\[negamax(a, b) = -negamax(-a, -b)\]

How It Works

  1. Recursive search to specified depth
  2. Evaluate leaf nodes using material + PST
  3. Propagate scores back up the tree

The result is an evaluation tree like this:

graph TD
	A1(("-6"))
	
	B1["-2"]
	B2["+6"]
	B3["-2"]
	
	C1(("+2"))
	C2(("-5"))
	C3(("-6"))
	C4(("+2"))
	C5(("+4"))
	C6(("+8"))
	
	D1["+5"]
	D2["-3"]
	D3["-2"]

	A1 --- B1
	A1 --- B2
	A1 --- B3
	
	B1 --- C1
	B1 --- C2
	
	B2 --- C3
	
	B3 --- C4
	B3 --- C5
	B3 --- C6
	
	C2 --- D1
	C2 --- D2
	
	C4 --- D3
	


Alpha-Beta Pruning: The Speed Boost ⚡

Alpha-beta pruning cuts off entire branches when a better solution is already known. This allows massive sections of the search tree to be skipped!

Performance Impact


Move Ordering: Making Pruning Effective

Pruning only works well if good moves are searched first.

NICE’s Move Ordering Strategy

  1. Captures - Forcing moves that win material
  2. Killer Moves - Previously successful quiet moves
  3. Quiet Moves - Everything else

Killer Move Heuristic

Killer moves are quiet moves that caused cutoffs in similar positions. By checking these first, we dramatically improve pruning efficiency.

Think of it as the engine’s memory of what worked before!


Summary

The NICE chess engine combines:

Result: ~1700 Elo performance comparable to Stockfish Level 5!

Challenge NICE on Lichess →
Back to Projects