shithub: moonfish

Download patch

ref: 2c0fcbf09a5faa2800421e792955222460c3a695
parent: 3a4dd039662549083dfb672a6a78910af84ccce7
author: zamfofex <zamfofex@twdb.moe>
date: Fri Jun 21 23:09:52 EDT 2024

improve minification

--- a/.gitignore
+++ b/.gitignore
@@ -34,8 +34,4 @@
 !/tools/https.c
 !/tools/perft.c
 !/tools/book.c
-!/extras
-!/extras/texel-tuner
-!/extras/moonfish.cc
-!/extras/moonfish.hh
-!/extras/config.h
+!/tools/learn.c
--- a/.gitmodules
+++ /dev/null
@@ -1,3 +1,0 @@
-[submodule "extras/texel-tuner"]
-	path = extras/texel-tuner
-	url = https://github.com/GediminasMasaitis/texel-tuner
--- a/chess.c
+++ b/chess.c
@@ -3,86 +3,26 @@
 
 #include "moonfish.h"
 
-static int moonfish_piece_square_scores[] =
-{
-	0, 0, 0, 0,
-	232, 240, 242, 229,
-	172, 171, 176, 148,
-	128, 121, 129, 114,
-	119, 111, 116, 98,
-	102, 108, 116, 95,
-	95, 109, 121, 90,
-	0, 0, 0, 0,
-	
-	330, 318, 308, 224,
-	360, 360, 324, 314,
-	386, 372, 366, 334,
-	371, 367, 352, 338,
-	347, 351, 339, 322,
-	337, 338, 322, 297,
-	319, 311, 301, 287,
-	299, 296, 289, 255,
-	
-	358, 357, 353, 339,
-	368, 366, 363, 343,
-	386, 381, 378, 372,
-	388, 378, 376, 362,
-	377, 373, 366, 358,
-	370, 369, 363, 355,
-	352, 356, 364, 350,
-	331, 327, 326, 337,
-	
-	583, 577, 576, 578,
-	593, 593, 585, 587,
-	588, 582, 579, 577,
-	574, 571, 569, 562,
-	554, 553, 552, 542,
-	540, 536, 535, 522,
-	537, 532, 524, 505,
-	553, 545, 529, 513,
-	
-	1117, 1111, 1099, 1077,
-	1124, 1121, 1095, 1097,
-	1136, 1124, 1118, 1103,
-	1114, 1113, 1098, 1107,
-	1080, 1088, 1091, 1099,
-	1076, 1080, 1077, 1069,
-	1076, 1075, 1069, 1062,
-	1065, 1050, 1050, 1071,
-	
-	30, 37, 43, -17,
-	33, 36, 52, 15,
-	24, 36, 42, 19,
-	7, 17, 20, 0,
-	-5, 1, -1, -17,
-	-15, -10, -9, -22,
-	-37, -14, -1, -1,
-	-14, -21, 16, -2,
-};
+moonfish_t moonfish_values[moonfish_size] = {0,0,0,0,138,180,159,139,137,167,147,150,135,159,159,167,170,190,176,191,222,260,267,253,313,370,387,366,0,0,0,0,311,363,377,386,366,390,408,413,382,416,436,433,416,448,459,462,431,459,483,483,435,479,491,505,402,418,469,477,307,390,403,431,431,422,411,426,452,467,461,456,466,470,482,483,473,475,483,493,470,485,492,508,489,483,496,505,441,465,476,483,442,451,462,465,653,686,713,726,660,684,687,698,680,703,700,711,709,726,728,729,736,755,757,757,760,781,785,777,780,772,790,785,762,764,759,775,1282,1267,1261,1274,1284,1289,1295,1297,1290,1300,1303,1301,1323,1338,1325,1325,1344,1328,1366,1361,1328,1368,1379,1392,1326,1324,1363,1384,1286,1306,1348,1351,-4,5,-51,-42,-9,-11,-30,-58,-37,-26,-36,-36,-44,-16,-17,-16,-11,14,9,-14,19,50,36,15,26,86,41,36,2,42,42,34};
 
-static int moonfish_table(int from, unsigned char piece)
+static void moonfish_table(struct moonfish_chess *chess, int from, unsigned char piece, int n)
 {
 	int x, y;
 	unsigned char type, color;
-	int score;
 	
-	if (piece == moonfish_empty) return 0;
+	if (piece == moonfish_empty) return;
 	
 	x = from % 10 - 1;
 	y = from / 10 - 2;
 	
-	type = (piece % 16) - 1;
-	color = (piece / 16) - 1;
+	type = piece % 16 - 1;
+	color = piece / 16 - 1;
+	n *= color * 2 - 1;
 	
-	if (color == 0) y = 7 - y;
+	if (x > 3) x = 7 - x;
+	if (color == 1) y = 7 - y;
 	
-	if (x < 4) x = 3 - x;
-	else x %= 4;
-	
-	score = moonfish_piece_square_scores[x + y * 4 + type * 32];
-	if (color != 0) score *= -1;
-	
-	return score;
+	chess->score -= moonfish_values[x + y * 4 + type * 32] * n;
 }
 
 static void moonfish_force_promotion(struct moonfish_chess *chess, struct moonfish_move **moves, unsigned char from, unsigned char to, unsigned char promotion)
@@ -90,18 +30,20 @@
 	(*moves)->from = from;
 	(*moves)->to = to;
 	(*moves)->chess = *chess;
+	
+	moonfish_table(&(*moves)->chess, from, chess->board[from], -1);
+	moonfish_table(&(*moves)->chess, to, promotion, 1);
+	moonfish_table(&(*moves)->chess, to, chess->board[to], -1);
+	
 	(*moves)->chess.board[to] = promotion;
 	(*moves)->chess.board[from] = moonfish_empty;
-	(*moves)->chess.score -= moonfish_table(from, chess->board[from]);
-	(*moves)->chess.score += moonfish_table(to, promotion);
-	(*moves)->chess.score -= moonfish_table(to, chess->board[to]);
 	(*moves)->chess.passing = 0;
 	(*moves)->chess.white ^= 1;
 	
-	if (from == 21 || to == 21) (*moves)->chess.white_ooo = 0;
-	if (from == 28 || to == 28) (*moves)->chess.white_oo = 0;
-	if (from == 91 || to == 91) (*moves)->chess.black_ooo = 0;
-	if (from == 98 || to == 98) (*moves)->chess.black_oo = 0;
+	if (from == 21 || to == 21) (*moves)->chess.ooo[0] = 0;
+	if (from == 28 || to == 28) (*moves)->chess.oo[0] = 0;
+	if (from == 91 || to == 91) (*moves)->chess.ooo[1] = 0;
+	if (from == 98 || to == 98) (*moves)->chess.oo[1] = 0;
 	
 	(*moves)++;
 }
@@ -111,7 +53,7 @@
 	moonfish_force_promotion(chess, moves, from, to, chess->board[from]);
 }
 
-static void moonfish_deltas(struct moonfish_chess *chess, struct moonfish_move **moves, unsigned char from, int *deltas, int n)
+static void moonfish_deltas(struct moonfish_chess *chess, struct moonfish_move **moves, unsigned char from, int *deltas, int count, int n)
 {
 	int i;
 	unsigned char to;
@@ -119,9 +61,9 @@
 	while (*deltas)
 	{
 		to = from;
-		for (i = 0 ; i < n ; i++)
+		for (i = 0 ; i < count ; i++)
 		{
-			to += *deltas;
+			to += *deltas * n;
 			if (chess->board[to] == moonfish_outside) break;
 			if (chess->board[to] / 16 == chess->board[from] / 16) break;
 			moonfish_force_move(chess, moves, from, to);
@@ -131,20 +73,52 @@
 	}
 }
 
+int moonfish_moves(struct moonfish_chess *chess, struct moonfish_move *moves, unsigned char from);
+
+int moonfish_validate(struct moonfish_chess *chess)
+{
+	int x, y;
+	struct moonfish_move moves[32];
+	int i, count;
+	
+	for (y = 0 ; y < 8 ; y++)
+	for (x = 0 ; x < 8 ; x++)
+	{
+		count = moonfish_moves(chess, moves, (x + 1) + (y + 2) * 10);
+		for (i = 0 ; i < count ; i++)
+			if (chess->board[moves[i].to] % 16 == moonfish_king)
+				return 0;
+	}
+	
+	return 1;
+}
+
+int moonfish_check(struct moonfish_chess *chess)
+{
+	struct moonfish_chess other;
+	
+	other = *chess;
+	other.passing = 0;
+	other.oo[0] = 0;
+	other.oo[1] = 0;
+	other.ooo[0] = 0;
+	other.ooo[1] = 0;
+	other.white ^= 1;
+	return moonfish_validate(&other) ^ 1;
+}
+
 static char moonfish_attacked(struct moonfish_chess *chess, unsigned char from, unsigned char to)
 {
-	int check;
 	unsigned char piece;
+	struct moonfish_chess other;
 	
 	if (chess->white) piece = moonfish_white_king;
 	else piece = moonfish_black_king;
 	
-	chess->board[from] = moonfish_empty;
-	chess->board[to] = piece;
-	check = moonfish_check(chess);
-	chess->board[from] = piece;
-	chess->board[to] = moonfish_empty;
-	return check;
+	other = *chess;
+	other.board[from] = moonfish_empty;
+	other.board[to] = piece;
+	return moonfish_check(&other);
 }
 
 static void moonfish_castle_low(struct moonfish_chess *chess, struct moonfish_move **moves, unsigned char from)
@@ -151,6 +125,8 @@
 {
 	unsigned char to;
 	
+	if (!chess->ooo[1 - chess->white]) return;
+	
 	to = from - 3;
 	while (to != from)
 		if (chess->board[to++] != moonfish_empty)
@@ -170,6 +146,8 @@
 {
 	unsigned char to;
 	
+	if (!chess->oo[1 - chess->white]) return;
+	
 	to = from + 2;
 	while (to != from)
 		if (chess->board[to--] != moonfish_empty)
@@ -185,20 +163,6 @@
 	moonfish_force_move(&(*moves)->chess, moves, from, from + 2);
 }
 
-static void moonfish_move_king(struct moonfish_chess *chess, struct moonfish_move **moves, unsigned char from)
-{
-	if (chess->white)
-	{
-		if (chess->white_oo) moonfish_castle_high(chess, moves, from);
-		if (chess->white_ooo) moonfish_castle_low(chess, moves, from);
-	}
-	else
-	{
-		if (chess->black_oo) moonfish_castle_high(chess, moves, from);
-		if (chess->black_ooo) moonfish_castle_low(chess, moves, from);
-	}
-}
-
 static void moonfish_pawn_moves(struct moonfish_chess *chess, struct moonfish_move **moves, unsigned char from, unsigned char to)
 {
 	unsigned char color;
@@ -225,7 +189,7 @@
 		dy = chess->white ? 10 : -10;
 		
 		moonfish_force_move(chess, moves, from, to);
-		(*moves)[-1].chess.score -= moonfish_table(to - dy, chess->board[to - dy]);
+		moonfish_table(&(*moves)[-1].chess, to - dy, chess->board[to - dy], -1);
 		(*moves)[-1].chess.board[to - dy] = moonfish_empty;
 		return;
 	}
@@ -261,14 +225,14 @@
 int moonfish_moves(struct moonfish_chess *chess, struct moonfish_move *moves, unsigned char from)
 {
 	static int steps[] = {0, 1, 8, 8, 8, 1};
-	static int deltas[][9] =
+	static int deltas[][5] =
 	{
 		{0},
-		{21, 19, -19, -21, 12, 8, -8, -12, 0},
-		{11, 9, -9, -11, 0},
-		{10, -10, 1, -1, 0},
-		{10, -10, 1, -1, 11, 9, -9, -11, 0},
-		{10, -10, 1, -1, 11, 9, -9, -11, 0},
+		{21, 19, 12, 8, 0},
+		{11, 9, 0},
+		{10, 1, 0},
+		{10, 1, 11, 9, 0},
+		{10, 1, 11, 9, 0},
 	};
 	
 	struct moonfish_move *moves0;
@@ -280,28 +244,22 @@
 	
 	if (chess->white ? piece / 16 == 1 : piece / 16 == 2)
 	{
-		moonfish_deltas(chess, &moves, from, deltas[piece % 16 - 1], steps[piece % 16 - 1]);
+		moonfish_deltas(chess, &moves, from, deltas[piece % 16 - 1], steps[piece % 16 - 1], 1);
+		moonfish_deltas(chess, &moves, from, deltas[piece % 16 - 1], steps[piece % 16 - 1], -1);
 		
 		if (piece % 16 == moonfish_pawn) moonfish_move_pawn(chess, &moves, from);
 		
 		if (piece % 16 == moonfish_king)
 		{
-			moonfish_move_king(chess, &moves, from);
+			moonfish_castle_high(chess, &moves, from);
+			moonfish_castle_low(chess, &moves, from);
 			
 			count = moves - moves0;
 			
 			for (i = 0 ; i < count ; i++)
 			{
-				if (chess->white)
-				{
-					moves0[i].chess.white_oo = 0;
-					moves0[i].chess.white_ooo = 0;
-				}
-				else
-				{
-					moves0[i].chess.black_oo = 0;
-					moves0[i].chess.black_ooo = 0;
-				}
+				moves0[i].chess.oo[1 - chess->white] = 0;
+				moves0[i].chess.ooo[1 - chess->white] = 0;
 			}
 		}
 	}
@@ -311,16 +269,16 @@
 
 void moonfish_chess(struct moonfish_chess *chess)
 {
-	char pieces[] = {moonfish_rook, moonfish_knight, moonfish_bishop, moonfish_queen, moonfish_king, moonfish_bishop, moonfish_knight, moonfish_rook};
+	static unsigned char pieces[] = {moonfish_rook, moonfish_knight, moonfish_bishop, moonfish_queen, moonfish_king, moonfish_bishop, moonfish_knight, moonfish_rook};
 	int x, y;
 	
 	chess->white = 1;
-	chess->white_oo = 1;
-	chess->white_ooo = 1;
-	chess->black_oo = 1;
-	chess->black_ooo = 1;
-	chess->score = 0;
+	chess->oo[0] = 1;
+	chess->oo[1] = 1;
+	chess->ooo[0] = 1;
+	chess->ooo[1] = 1;
 	chess->passing = 0;
+	chess->score = 0;
 	
 	for (y = 0 ; y < 12 ; y++)
 	for (x = 0 ; x < 10 ; x++)
@@ -328,10 +286,10 @@
 	
 	for (x = 0 ; x < 8 ; x++)
 	{
-		chess->board[(x + 1) + 20] = pieces[x] | 0x10;
-		chess->board[(x + 1) + 90] = pieces[x] | 0x20;
-		chess->board[(x + 1) + 30] = moonfish_white_pawn;
-		chess->board[(x + 1) + 80] = moonfish_black_pawn;
+		chess->board[x + 21] = pieces[x] | 0x10;
+		chess->board[x + 91] = pieces[x] | 0x20;
+		chess->board[x + 31] = moonfish_white_pawn;
+		chess->board[x + 81] = moonfish_black_pawn;
 		
 		for (y = 4 ; y < 8 ; y++)
 			chess->board[(x + 1) + y * 10] = moonfish_empty;
@@ -342,7 +300,7 @@
 {
 	int x0, y0;
 	int x1, y1;
-	unsigned char piece, color;
+	unsigned char type;
 	unsigned char from, to;
 	int i, count;
 	struct moonfish_move moves[32];
@@ -359,35 +317,20 @@
 	x1 = name[2] - 'a';
 	y1 = name[3] - '1';
 	
-	piece = chess->board[(x0 + 1) + (y0 + 2) * 10];
-	if (piece % 16 == moonfish_king && x0 == 4)
+	type = chess->board[(x0 + 1) + (y0 + 2) * 10] % 16;
+	if (type == moonfish_king && x0 == 4)
 	{
 		if (x1 == 0) x1 = 2;
 		if (x1 == 7) x1 = 6;
 	}
 	
-	color = piece & 0xF0;
+	if (name[4] == 0) { }
+	else if (name[4] == 'q') type = moonfish_queen;
+	else if (name[4] == 'r') type = moonfish_rook;
+	else if (name[4] == 'b') type = moonfish_bishop;
+	else if (name[4] == 'n') type = moonfish_knight;
+	else return 1;
 	
-	switch (name[4])
-	{
-	default:
-		return 1;
-	case 0:
-		break;
-	case 'q':
-		piece = color | moonfish_queen;
-		break;
-	case 'r':
-		piece = color | moonfish_rook;
-		break;
-	case 'b':
-		piece = color | moonfish_bishop;
-		break;
-	case 'n':
-		piece = color | moonfish_knight;
-		break;
-	}
-	
 	from = (x0 + 1) + (y0 + 2) * 10;
 	to = (x1 + 1) + (y1 + 2) * 10;
 	
@@ -396,7 +339,7 @@
 	for (i = 0 ; i < count ; i++)
 	{
 		if (moves[i].to != to) continue;
-		if (moves[i].chess.board[to] != piece) continue;
+		if (moves[i].chess.board[to] % 16 != type) continue;
 		*move = moves[i];
 		return 0;
 	}
@@ -435,55 +378,6 @@
 	}
 }
 
-int moonfish_validate(struct moonfish_chess *chess)
-{
-	int x, y;
-	struct moonfish_move moves[32];
-	int i, count;
-	
-	for (y = 0 ; y < 8 ; y++)
-	for (x = 0 ; x < 8 ; x++)
-	{
-		count = moonfish_moves(chess, moves, (x + 1) + (y + 2) * 10);
-		for (i = 0 ; i < count ; i++)
-			if (chess->board[moves[i].to] % 16 == moonfish_king)
-				return 0;
-	}
-	
-	return 1;
-}
-
-int moonfish_check(struct moonfish_chess *chess)
-{
-	int valid;
-	unsigned char passing;
-	int white_oo, white_ooo;
-	int black_oo, black_ooo;
-	
-	passing = chess->passing;
-	white_oo = chess->white_oo;
-	white_ooo = chess->white_ooo;
-	black_oo = chess->black_oo;
-	black_ooo = chess->black_ooo;
-	
-	chess->white_oo = 0;
-	chess->white_ooo = 0;
-	chess->black_oo = 0;
-	chess->black_ooo = 0;
-	
-	chess->white ^= 1;
-	valid = moonfish_validate(chess);
-	chess->white ^= 1;
-	
-	chess->passing = passing;
-	chess->white_oo = white_oo;
-	chess->white_ooo = white_ooo;
-	chess->black_oo = black_oo;
-	chess->black_ooo = black_ooo;
-	
-	return valid ^ 1;
-}
-
 #ifndef moonfish_mini
 
 #include <string.h>
@@ -524,12 +418,12 @@
 	y = 0;
 	
 	chess->white = 1;
-	chess->white_oo = 0;
-	chess->white_ooo = 0;
-	chess->black_oo = 0;
-	chess->black_ooo = 0;
-	chess->score = 0;
+	chess->oo[0] = 0;
+	chess->oo[1] = 0;
+	chess->ooo[0] = 0;
+	chess->ooo[1] = 0;
 	chess->passing = 0;
+	chess->score = 0;
 	
 	for (;;)
 	{
@@ -566,9 +460,8 @@
 		if (ch == 'k') type = 6;
 		
 		chess->board[(x + 1) + (9 - y) * 10] = type | color;
+		moonfish_table(chess, (x + 1) + (9 - y) * 10, type | color, 1);
 		
-		chess->score += moonfish_table((x + 1) + (9 - y) * 10, type | color);
-		
 		x++;
 	}
 	
@@ -582,10 +475,10 @@
 		if (ch == 0) return 0;
 		if (ch == ' ') break;
 		
-		if (ch == 'K') chess->white_oo = 1;
-		if (ch == 'Q') chess->white_ooo = 1;
-		if (ch == 'k') chess->black_oo = 1;
-		if (ch == 'q') chess->black_ooo = 1;
+		if (ch == 'K') chess->oo[0] = 1;
+		if (ch == 'k') chess->oo[1] = 1;
+		if (ch == 'Q') chess->ooo[0] = 1;
+		if (ch == 'q') chess->ooo[1] = 1;
 		
 		if (ch >= 'A' && ch <= 'H') return 1;
 		if (ch >= 'a' && ch <= 'h') return 1;
@@ -860,10 +753,10 @@
 	else *fen++ = 'b';
 	*fen++ = ' ';
 	
-	if (chess->white_oo) *fen++ = 'K';
-	if (chess->white_ooo) *fen++ = 'Q';
-	if (chess->black_oo) *fen++ = 'k';
-	if (chess->black_ooo) *fen++ = 'q';
+	if (chess->oo[0]) *fen++ = 'K';
+	if (chess->ooo[0]) *fen++ = 'Q';
+	if (chess->oo[1]) *fen++ = 'k';
+	if (chess->ooo[1]) *fen++ = 'q';
 	if (fen[-1] == ' ') *fen++ = '-';
 	
 	*fen++ = ' ';
--- a/extras/config.h
+++ /dev/null
@@ -1,15 +1,0 @@
-/* moonfish is licensed under the AGPL (v3 or later) */
-/* copyright 2024 zamfofex */
-
-#ifndef MOONFISH_TEXEL_TUNER_CONFIG
-#define MOONFISH_TEXEL_TUNER_CONFIG
-
-#include "moonfish.hh"
-
-using TuneEval = moonfish::MoonfishEval;
-constexpr int data_load_thread_count = 4;
-constexpr int thread_count = 4;
-constexpr static bool print_data_entries = false;
-constexpr static int data_load_print_interval = 10000;
-
-#endif
--- a/extras/moonfish.cc
+++ /dev/null
@@ -1,98 +1,0 @@
-/* moonfish is licensed under the AGPL (v3 or later) */
-/* copyright 2024 zamfofex */
-
-#include <math.h>
-#include <stdio.h>
-
-#include "moonfish.hh"
-
-extern "C"
-{
-#include "../moonfish.h"
-}
-
-struct moonfish_trace
-{
-	int values[32 * 6][2];
-};
-
-parameters_t moonfish::MoonfishEval::get_initial_parameters(void)
-{
-	static int scores[6 * 32] = {0};
-	parameters_t parameters;
-	get_initial_parameter_array(parameters, scores, 6 * 32);
-	return parameters;
-}
-
-void moonfish::MoonfishEval::print_parameters(parameters_t parameters)
-{
-	int x, y, type;
-	long int l;
-	
-	printf("static int moonfish_piece_square_scores[] =\n{\n");
-	
-	for (type = 0 ; type < 6 ; type++)
-	{
-		for (y = 0 ; y < 8 ; y++)
-		{
-			printf("\t");
-			for (x = 0 ; x < 4 ; x++)
-			{
-				l = lround(parameters[x + y * 4 + type * 32]);
-				printf("%ld,", l);
-				if (x != 7) printf(" ");
-			}
-			printf("\n");
-		}
-		if (type != 5) printf("\n");
-	}
-	
-	printf("};\n");
-}
-
-
-EvalResult moonfish::MoonfishEval::get_fen_eval_result(std::string fen)
-{
-	struct moonfish_chess chess;
-	int x0, y0;
-	int x, y;
-	struct moonfish_trace trace = {0};
-	unsigned char type, color;
-	unsigned char piece;
-	EvalResult result;
-	
-	moonfish_chess(&chess);
-	moonfish_from_fen(&chess, (char *) fen.c_str());
-	
-	for (y0 = 0 ; y0 < 8 ; y0++)
-	for (x0 = 0 ; x0 < 8 ; x0++)
-	{
-		x = x0;
-		y = y0;
-		
-		piece = chess.board[(x + 1) + (y + 2) * 10];
-		if (piece == moonfish_empty) continue;
-		
-		type = (piece % 16) - 1;
-		color = (piece / 16) - 1;
-		
-		if (color == 0) y = 7 - y;
-		
-		if (x < 4) x = 3 - x;
-		else x %= 4;
-		
-		trace.values[x + y * 4 + type * 32][color]++;
-	}
-	
-	get_coefficient_array(result.coefficients, trace.values, 6 * 32);
-	result.score = 0;
-	result.endgame_scale = 0;
-	return result;
-}
-
-EvalResult moonfish::MoonfishEval::get_external_eval_result(chess::Board board)
-{
-	(void) board;
-	EvalResult result;
-	return result;
-}
--- a/extras/moonfish.hh
+++ /dev/null
@@ -1,39 +1,0 @@
-/* moonfish is licensed under the AGPL (v3 or later) */
-/* copyright 2024 zamfofex */
-
-#ifndef MOONFISH_TEXEL_TUNER
-#define MOONFISH_TEXEL_TUNER
-
-#define TAPERED 0
-
-#include <string>
-
-#include "texel-tuner/src/base.h"
-#include "texel-tuner/src/external/chess.hpp"
-
-namespace moonfish
-{
-	class MoonfishEval
-	{
-	public:
-		constexpr static bool includes_additional_score = true;
-		constexpr static bool supports_external_chess_eval = false;
-		constexpr static bool retune_from_zero = true;
-		constexpr static tune_t preferred_k = 2.1;
-		constexpr static int32_t max_epoch = 5001;
-		constexpr static bool enable_qsearch = false;
-		constexpr static bool filter_in_check = false;
-		constexpr static tune_t initial_learning_rate = 1;
-		constexpr static int32_t learning_rate_drop_interval = 10000;
-		constexpr static tune_t learning_rate_drop_ratio = 1;
-		constexpr static bool print_data_entries = false;
-		constexpr static int32_t data_load_print_interval = 10000;
-		
-		static parameters_t get_initial_parameters(void);
-		static EvalResult get_fen_eval_result(std::string fen);
-		static EvalResult get_external_eval_result(chess::Board board);
-		static void print_parameters(parameters_t parameters);
-	};
-}
-
-#endif
--- a/main.c
+++ b/main.c
@@ -12,7 +12,6 @@
 {
 	static char line[2048];
 	
-	struct moonfish_analysis *analysis;
 	char *arg;
 	struct moonfish_move move;
 	char name[6];
@@ -25,11 +24,10 @@
 	
 	if (argc > 1)
 	{
-		if (argc > 0) fprintf(stderr, "usage: %s\n", argv[0]);
+		fprintf(stderr, "usage: %s\n", argv[0]);
 		return 1;
 	}
 	
-	analysis = moonfish_analysis(argv[0]);
 	moonfish_chess(&chess);
 	
 	for (;;)
@@ -141,11 +139,11 @@
 			if (their_time < 0) their_time = 0;
 			
 			if (depth >= 0)
-				score = moonfish_best_move_depth(analysis, &move, depth);
+				score = moonfish_best_move_depth(&chess, &move, depth);
 			else if (time >= 0)
-				score = moonfish_best_move_time(analysis, &move, time);
+				score = moonfish_best_move_time(&chess, &move, time);
 			else
-				score = moonfish_best_move_clock(analysis, &move, our_time, their_time);
+				score = moonfish_best_move_clock(&chess, &move, our_time, their_time);
 			
 			if (depth < 0) depth = 4;
 			printf("info depth %d ", depth);
@@ -217,8 +215,6 @@
 					chess = move.chess;
 				}
 			}
-			
-			moonfish_new(analysis, &chess);
 		}
 		else if (!strcmp(arg, "uci"))
 		{
@@ -240,8 +236,6 @@
 		
 		fflush(stdout);
 	}
-	
-	free(analysis);
 	
 	return 0;
 }
--- a/makefile
+++ b/makefile
@@ -2,12 +2,10 @@
 # copyright 2023, 2024 zamfofex
 
 CFLAGS ?= -ansi -O3 -Wall -Wextra -Wpedantic
-CXXFLAGS ?= -std=c++20 -O3 -Wall -Wextra -Wpedantic
 PREFIX ?= /usr/local
 BINDIR ?= $(PREFIX)/bin
 
 cc := $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS)
-cxx := $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS)
 
 moonfish_cc := $(cc) -pthread -D_POSIX_C_SOURCE=199309L
 tools_cc := $(cc) -pthread -D_POSIX_C_SOURCE=200809L
@@ -37,20 +35,8 @@
 uci-ugi: $(ugi_src)
 	$(tools_cc) -o $@ $(filter %.c,$^)
 
-tuner: \
-	extras/texel-tuner/src/main.cpp \
-	extras/texel-tuner/src/tuner.cpp \
-	extras/texel-tuner/src/threadpool.cpp \
-	extras/texel-tuner/src/base.h \
-	extras/texel-tuner/src/threadpool.h \
-	extras/texel-tuner/src/tuner.h \
-	extras/moonfish.cc \
-	extras/moonfish.hh \
-	extras/config.h \
-	chess.c moonfish.h
-	$(RM) extras/texel-tuner/src/config.h
-	$(cc) -c chess.c
-	$(cxx) -pthread -iquote extras -o $@ $(filter %.cc %.cpp,$^) chess.o
+learn: $(tools_src) tools/learn.c
+	$(tools_cc) -Dmoonfish_learn -o $@ $(filter %.c,$^)
 
 clean:
 	git clean -fdx
--- a/mini.c
+++ b/mini.c
@@ -11,16 +11,12 @@
 {
 	static char line[2048];
 	
-	struct moonfish_analysis *analysis;
 	struct moonfish_move move;
 	char name[6];
 	int our_time, their_time, time;
-	int score;
 	struct moonfish_chess chess;
 	char *arg;
 	
-	analysis = moonfish_analysis(NULL);
-	
 	for (;;)
 	{
 		fgets(line, sizeof line, stdin);
@@ -38,15 +34,7 @@
 				their_time = time;
 			}
 			
-			score = moonfish_best_move_clock(analysis, &move, our_time, their_time);
-			
-			printf("info depth 4 ");
-			
-			if (score >= moonfish_omega || score <= -moonfish_omega)
-				printf("score mate %d\n", moonfish_countdown(score));
-			else
-				printf("score cp %d\n", score);
-			
+			moonfish_best_move_clock(&chess, &move, our_time, their_time);
 			moonfish_to_uci(&chess, &move, name);
 			printf("bestmove %s\n", name);
 		}
@@ -59,13 +47,11 @@
 			
 			arg = strtok(arg, " ");
 			
-			while ((arg = strtok(NULL, "\r\n\t ")) != NULL)
+			while (arg = strtok(NULL, "\n "))
 			{
 				moonfish_from_uci(&chess, &move, arg);
 				chess = move.chess;
 			}
-			
-			moonfish_new(analysis, &chess);
 		}
 		else if (!strcmp(line, "uci"))
 			printf("uciok\n");
@@ -76,6 +62,4 @@
 		
 		fflush(stdout);
 	}
-	
-	return 0;
 }
--- a/minify.sh
+++ b/minify.sh
@@ -5,15 +5,30 @@
 
 set -e
 
-# for every C source file
+# for each C source file
 cat moonfish.h chess.c search.c mini.c |
 
-# remove the '#' from system '#include'
-sed 's/^#\(include <\)/\1/g' |
+# replace 'unsigned char' with 'int'
+sed 's/\bunsigned char\b/int/g' |
 
-# remove top-level 'static'
+# remove 'signed' and 'unsigned'
+sed 's/\tsigned /\t/g' |
+sed 's/\tunsigned /\t/g' |
+
+# remove 'long'
+sed 's/\blong\b \?//g' |
+
+# remove top-level 'static', 'int' and 'void'
 sed 's/^static\b//g' |
+sed 's/^int\b//g' |
+sed 's/^void\b//g' |
 
+#remove redundant 'int'
+sed 's/\bstatic int\b/static/g' |
+
+# remove the '#' from system '#include'
+sed 's/^#\(include <\)/\1/g' |
+
 # preprocess the file, add '#' back to 'include'
 # note: this materialises the whole file
 gcc -E -Dinclude='#include' -Dmoonfish_mini - |
@@ -68,13 +83,13 @@
 tee moonfish.c |
 
 # and also compress it
-xz -9 -e > moonfish.c.xz
+xz -e9qFraw > moonfish.c.xz
 
-# also make it into a runnable program
+# finally, make it into an executable program
 cat - moonfish.c.xz > moonfish.sh << END
 #!/bin/sh
 t=\`mktemp\`
-tail -n +5 "\$0"|xz -d|cc -march=native -O3 -o \$t -xc - -pthread
+gcc -O3 -o \$t -xc <(tail -n+5 "\$0"|unxz -Fraw)
 (sleep 3;rm \$t)&exec \$t
 END
 chmod +x moonfish.sh
--- a/moonfish.h
+++ b/moonfish.h
@@ -52,43 +52,50 @@
 
 /* the board is not just an 8 x 8 array because of an optisation that can be performed when generating moves */
 
-enum
-{
-	/* white pieces */
-	moonfish_white_pawn = 0x11,
-	moonfish_white_knight = 0x12,
-	moonfish_white_bishop = 0x13,
-	moonfish_white_rook = 0x14,
-	moonfish_white_queen = 0x15,
-	moonfish_white_king = 0x16,
-	
-	/* black pieces */
-	moonfish_black_pawn = 0x21,
-	moonfish_black_knight = 0x22,
-	moonfish_black_bishop = 0x23,
-	moonfish_black_rook = 0x24,
-	moonfish_black_queen = 0x25,
-	moonfish_black_king = 0x26,
-	
-	/* piece types (without colors) */
-	moonfish_pawn = 1,
-	moonfish_knight = 2,
-	moonfish_bishop = 3,
-	moonfish_rook = 4,
-	moonfish_queen = 5,
-	moonfish_king = 6,
-	
-	/* special values within the board representation */
-	moonfish_outside = 0,
-	moonfish_empty = 0xFF,
-	
-	/* constants for search */
-	/* depth: the maximum depth considerable feasibly reachable (in practice, it's much lower!) */
-	/* omega: high value (used as integral infinity) */
-	moonfish_depth = 50,
-	moonfish_omega = 5000000
-};
+/* white pieces */
+#define moonfish_white_pawn 0x11
+#define moonfish_white_knight 0x12
+#define moonfish_white_bishop 0x13
+#define moonfish_white_rook 0x14
+#define moonfish_white_queen 0x15
+#define moonfish_white_king 0x16
+
+/* black pieces */
+#define moonfish_black_pawn 0x21
+#define moonfish_black_knight 0x22
+#define moonfish_black_bishop 0x23
+#define moonfish_black_rook 0x24
+#define moonfish_black_queen 0x25
+#define moonfish_black_king 0x26
+
+/* piece types (without colors) */
+#define moonfish_pawn 1
+#define moonfish_knight 2
+#define moonfish_bishop 3
+#define moonfish_rook 4
+#define moonfish_queen 5
+#define moonfish_king 6
+
+/* special values within the board representation */
+#define moonfish_outside 0
+#define moonfish_empty 0xFF
+
+/* constants for search */
+/* depth: the maximum depth considerable feasibly reachable (in practice, it's much lower!) */
+/* omega: high value (used as integral infinity) */
+#define moonfish_depth 50
+#define moonfish_omega 5000000
+
+/* size of the PST */
+#define moonfish_size 192
 
+/* for tuning the PST */
+#ifdef moonfish_learn
+#define moonfish_t double
+#else
+#define moonfish_t long int
+#endif
+
 /* represents a chess position */
 struct moonfish_chess
 {
@@ -95,13 +102,12 @@
 	/* 10 x 12 array board representation */
 	unsigned char board[120];
 	
-	/* bitfield booleans representing castling rights */
-	unsigned int white_oo:1, white_ooo:1;
-	unsigned int black_oo:1, black_ooo:1;
-	
 	/* PST score for the position */
-	int score;
+	moonfish_t score;
 	
+	/* booleans representing castling rights */
+	unsigned char oo[2], ooo[2];
+	
 	/* square index of a pawn that may be captured via e.p. */
 	/* or zero if there is no such pawn */
 	unsigned char passing;
@@ -122,11 +128,11 @@
 	unsigned char from, to;
 };
 
-/* opaque analysis struct */
-/* a value may be created (with "malloc") by using the functions below, and then freed by using "free" */
-/* this contains information that may be used during analysis that may be kept across evaluations of positions */
-struct moonfish_analysis;
+#ifndef moonfish_mini
 
+/* PST */
+extern moonfish_t moonfish_values[];
+
 /* initialises the position and sets up the initial position */
 /* note: this must be called *first* even if you want to use "moonfish_from_fen" */
 void moonfish_chess(struct moonfish_chess *chess);
@@ -144,29 +150,19 @@
 /* the move is stored in the "move" pointer, and the score for the position is returned */
 /* the move found is the best for the player whose turn it is on the given position */
 /* likewise, the score returned is from the perspective of the player whose turn it is */
-int moonfish_best_move_time(struct moonfish_analysis *analysis, struct moonfish_move *move, long int time);
+int moonfish_best_move_time(struct moonfish_chess *chess, struct moonfish_move *move, long int time);
 
 /* same as above, but tries to optimises the time spent searching for the given time left on each player's clock */
-int moonfish_best_move_clock(struct moonfish_analysis *analysis, struct moonfish_move *move, long int our_time, long int their_time);
+int moonfish_best_move_clock(struct moonfish_chess *chess, struct moonfish_move *move, long int our_time, long int their_time);
 
+/* tries to find the best move on the given position with a given depth */
+/* similar to "moonfish_best_move_time" and "moonfish_best_move_clock" */
+int moonfish_best_move_depth(struct moonfish_chess *chess, struct moonfish_move *move, int depth);
+
 /* if a score is too large (i.e. "score >= moonfish_omega"), it will instead represent a "checkmate in X" evaluation */
 /* this function will obtain such "X" from the given score in that case */
 int moonfish_countdown(int score);
 
-/* creates an analysis, which may be used to analyse many positions */
-/* note: in the future, the analysis might hold information across analyses (such as a transposition table) */
-/* but currently, that is not implemented */
-/* the analysis is created using "malloc", so don't forget to call "free" on it once you don't need it anymore */
-/* the analysis will be set up for analysing the initial position (but you may change this, see below) */
-/* the give "argv0" is used to report errors if necessary */
-/* a reference to "argv0" is kept, so it should be available for as long as the analysis is being used */
-/* on the case of an error (while creating the analysis, or even just using it), the program will be stopped with "exit" */
-struct moonfish_analysis *moonfish_analysis(char *argv0);
-
-/* sets up an existing analysis (created with the function above) to analyse the given position */
-/* a reference to the given position pointer will not be kept */
-void moonfish_new(struct moonfish_analysis *analysis, struct moonfish_chess *chess);
-
 /* creates a move from UCI notation */
 /* the move is stored in "move" */
 /* on success, the parser will return 0, on failure, it will return 1 (and the move is unusable) */
@@ -185,12 +181,6 @@
 
 /* returns whether player to play is in check (i.e. whether the current player's king is attacked on the given position) */
 int moonfish_check(struct moonfish_chess *chess);
-
-#ifndef moonfish_mini
-
-/* tries to find the best move on the position with a given depth */
-/* similar to "moonfish_best_move_time" and "moonfish_best_move_clock" */
-int moonfish_best_move_depth(struct moonfish_analysis *analysis, struct moonfish_move *move, int depth);
 
 /* uses the position from the given FEN, mutating the given position */
 /* returns 0 if the FEN could be parsed */
--- a/rename.sh
+++ b/rename.sh
@@ -9,8 +9,8 @@
 alphabet2=("${alphabet[@]}")
 declare -A names
 
-functions="main printf fprintf sscanf fgets fflush stdin stdout stderr strcmp strncmp strcpy strtok strstr strchr malloc realloc free exit errno clock_gettime timespec tv_sec tv_nsec pthread_create pthread_join pthread_t typedef moonfish_type_t"
-keywords="do while for if else switch case break continue return static struct enum unsigned signed long short int char void sizeof $functions"
+functions="main fopen fread printf fprintf sscanf fgets fflush stdin stdout stderr strcmp strncmp strcpy strtok strstr strchr malloc realloc free exit errno clock_gettime timespec tv_sec tv_nsec pthread_create pthread_join pthread_t typedef moonfish_type_t"
+keywords="do while for if else switch case break continue return extern static struct enum unsigned signed long short int char void sizeof $functions"
 
 while read -r name
 do
@@ -29,9 +29,7 @@
 	short="${names["$name"]}"
 	if [[ "$short" = "" ]]
 	then
-		if [[ "$name" = moonfish ]]
-		then short=F
-		elif [[ "$name" =~ ^moonfish ]]
+		if [[ "$name" =~ ^moonfish ]]
 		then
 			short="F${alphabet[0]}"
 			alphabet=("${alphabet[@]:1}")
--- a/search.c
+++ b/search.c
@@ -38,7 +38,7 @@
 #else
 
 #include <pthread.h>
-typedef void *moonfish_result_t;
+#define moonfish_result_t void *
 #define moonfish_value NULL
 
 #endif
@@ -56,7 +56,6 @@
 
 struct moonfish_analysis
 {
-	char *argv0;
 	struct moonfish_chess chess;
 	struct moonfish_info info[256];
 	int score;
@@ -66,15 +65,14 @@
 
 #ifdef _WIN32
 
-static long int moonfish_clock(struct moonfish_analysis *analysis)
+static long int moonfish_clock(void)
 {
-	(void) analysis;
 	return GetTickCount();
 }
 
 #else
 
-static long int moonfish_clock(struct moonfish_analysis *analysis)
+static long int moonfish_clock(void)
 {
 	struct timespec ts;
 	
@@ -83,7 +81,7 @@
 #else
 	if (clock_gettime(CLOCK_MONOTONIC, &ts))
 	{
-		perror(analysis->argv0);
+		perror(NULL);
 		exit(1);
 	}
 #endif
@@ -93,35 +91,6 @@
 
 #endif
 
-struct moonfish_analysis *moonfish_analysis(char *argv0)
-{
-	struct moonfish_analysis *analysis;
-	struct moonfish_chess chess;
-	
-	analysis = malloc(sizeof *analysis);
-#ifndef moonfish_mini
-	if (analysis == NULL)
-	{
-		perror(argv0);
-		exit(1);
-	}
-#endif
-	
-	analysis->argv0 = argv0;
-	
-	moonfish_chess(&chess);
-	moonfish_new(analysis, &chess);
-	
-	return analysis;
-}
-
-void moonfish_new(struct moonfish_analysis *analysis, struct moonfish_chess *chess)
-{
-	analysis->chess = *chess;
-	analysis->depth = 1;
-	analysis->time = -1;
-}
-
 static int moonfish_search(struct moonfish_info *info, struct moonfish_chess *chess, struct moonfish_move *moves, int alpha, int beta, int depth, long int t0, long int time)
 {
 	int score;
@@ -142,7 +111,7 @@
 		if (score >= beta) return beta;
 		if (score > alpha) alpha = score;
 	}
-	else if (info->analysis->time >= 0 && time < 0)
+	else if (info->analysis->time >= 0 && time < 5)
 	{
 		depth = 0;
 	}
@@ -164,7 +133,7 @@
 		if (moves[i].chess.board[moves[i].to] == chess->board[moves[i].from])
 			continue;
 		
-		t1 = moonfish_clock(info->analysis);
+		t1 = moonfish_clock();
 		c = time * i / count - t1 + t0;
 		
 		score = -moonfish_search(info, &moves[i].chess, moves + count, -beta, -alpha, depth - 1, t1, time / count + c);
@@ -176,27 +145,29 @@
 	return alpha;
 }
 
+#ifndef moonfish_mini
+
 int moonfish_countdown(int score)
 {
-	score /= -moonfish_omega;
-	if (score < 0) score += moonfish_depth + 1;
-	else score -= moonfish_depth;
+	score /= moonfish_omega;
+	if (score < 0) score -= moonfish_depth + 1;
+	else score += moonfish_depth;
 	return score / 2;
 }
 
+#endif
+
 static moonfish_result_t moonfish_start_search(void *data)
 {
 	struct moonfish_info *info;
-	long int time, t0;
-	int depth;
 	
 	info = data;
+	info->score = -moonfish_search(
+		info, &info->move.chess, info->moves,
+		-100 * moonfish_omega, 100 * moonfish_omega,
+		info->analysis->depth, moonfish_clock(), info->analysis->time
+	);
 	
-	depth = info->analysis->depth;
-	t0 = moonfish_clock(info->analysis);
-	time = info->analysis->time;
-	
-	info->score = -moonfish_search(info, &info->move.chess, info->moves, -100 * moonfish_omega, 100 * moonfish_omega, depth, t0, time);
 	return moonfish_value;
 }
 
@@ -246,7 +217,7 @@
 #ifndef moonfish_mini
 			if (result)
 			{
-				fprintf(stderr, "%s: %s\n", analysis->argv0, strerror(result));
+				fprintf(stderr, "%s\n", strerror(result));
 				exit(1);
 			}
 #endif
@@ -263,7 +234,7 @@
 #ifndef moonfish_mini
 		if (result)
 		{
-			fprintf(stderr, "%s: %s\n", analysis->argv0, strerror(result));
+			fprintf(stderr, "%s\n", strerror(result));
 			exit(1);
 		}
 #endif
@@ -278,31 +249,38 @@
 
 #ifndef moonfish_mini
 
-int moonfish_best_move_depth(struct moonfish_analysis *analysis, struct moonfish_move *best_move, int depth)
+int moonfish_best_move_depth(struct moonfish_chess *chess, struct moonfish_move *best_move, int depth)
 {
-	analysis->depth = depth;
-	analysis->time = -1;
-	moonfish_iteration(analysis, best_move);
-	return analysis->score;
+	static struct moonfish_analysis analysis;
+	
+	analysis.chess = *chess;
+	analysis.depth = depth;
+	analysis.time = -1;
+	moonfish_iteration(&analysis, best_move);
+	return analysis.score;
 }
 
 #endif
 
-int moonfish_best_move_time(struct moonfish_analysis *analysis, struct moonfish_move *best_move, long int time)
+int moonfish_best_move_time(struct moonfish_chess *chess, struct moonfish_move *best_move, long int time)
 {
+	static struct moonfish_analysis analysis;
+	
+	analysis.chess = *chess;
 	time -= 125;
 	if (time < 10) time = 10;
-	analysis->depth = 16;
-	analysis->time = time;
-	moonfish_iteration(analysis, best_move);
-	return analysis->score;
+	analysis.depth = 16;
+	analysis.time = time;
+	moonfish_iteration(&analysis, best_move);
+	return analysis.score;
 }
 
-int moonfish_best_move_clock(struct moonfish_analysis *analysis, struct moonfish_move *best_move, long int our_time, long int their_time)
+int moonfish_best_move_clock(struct moonfish_chess *chess, struct moonfish_move *best_move, long int our_time, long int their_time)
 {
 	long int time0, time1;
+	
 	time0 = our_time / 16;
 	time1 = our_time - time0 - their_time * 7 / 8;
 	if (time1 < 0) time1 = 0;
-	return moonfish_best_move_time(analysis, best_move, time0 + time1);
+	return moonfish_best_move_time(chess, best_move, time0 + time1);
 }
--- /dev/null
+++ b/tools/learn.c
@@ -1,0 +1,153 @@
+/* moonfish is licensed under the AGPL (v3 or later) */
+/* copyright 2024 zamfofex */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../moonfish.h"
+#include "tools.h"
+
+static double moonfish_next_line(char *argv0, char *line, FILE *file)
+{
+	char *arg, *end;
+	double score;
+	
+	errno = 0;
+	if (fgets(line, 2048, file) == NULL)
+	{
+		if (errno)
+		{
+			perror(argv0);
+			exit(1);
+		}
+		
+		errno = 0;
+		rewind(file);
+		if (errno)
+		{
+			perror(argv0);
+			exit(1);
+		}
+	}
+	
+	arg = strrchr(line, ' ');
+	if (arg == NULL)
+	{
+		fprintf(stderr, "%s: improper FEN line\n", argv0);
+		exit(1);
+	}
+	
+	errno = 0;
+	score = strtod(arg + 1, &end);
+	if (errno != 0 || (*end != 0 && *end != '\n') || score > 10000 || score < -10000)
+	{
+		fprintf(stderr, "%s: unexpected score\n", argv0);
+		exit(1);
+	}
+	
+	return score;
+}
+
+static double moonfish_gradient(double *gradient, double score0, char *fen)
+{
+	int i;
+	double prev;
+	double score, error;
+	struct moonfish_chess chess;
+	
+	moonfish_chess(&chess);
+	moonfish_from_fen(&chess, fen);
+	score = chess.score;
+	error = score - score0;
+	
+	for (i = 0 ; i < moonfish_size ; i++)
+	{
+		prev = moonfish_values[i];
+		moonfish_values[i] += 1.0 / 256 / 256;
+		moonfish_from_fen(&chess, fen);
+		gradient[i] += (chess.score - score) * 256 * 256 * error;
+		moonfish_values[i] = prev;
+	}
+	
+	if (error < 0) error *= -1;
+	return error;
+}
+
+static double moonfish_step(char *argv0, FILE *file, double *gradient)
+{
+	static char line[2048];
+	
+	int i;
+	double score;
+	double error;
+	
+	error = 0;
+	
+	for (i = 0 ; i < moonfish_size ; i++) gradient[i] = 0;
+	
+	for (i = 0 ; i < 2048 ; i++)
+	{
+		score = moonfish_next_line(argv0, line, file);
+		error += moonfish_gradient(gradient, score, line);
+	}
+	
+	for (i = 0 ; i < moonfish_size ; i++)
+		moonfish_values[i] -= gradient[i] / 2048;
+	
+	return error;
+}
+
+int main(int argc, char **argv)
+{
+	static double gradient[moonfish_size];
+	
+	FILE *file;
+	int i;
+	double error;
+	int iteration;
+	
+	if (argc != 2)
+	{
+		if (argc > 0) fprintf(stderr, "usage: %s <file-name>\n", argv[0]);
+		return 1;
+	}
+	
+	file = fopen(argv[1], "r");
+	if (file == NULL)
+	{
+		perror(argv[0]);
+		return 1;
+	}
+	
+	errno = 0;
+	rewind(file);
+	if (errno)
+	{
+		perror(argv[0]);
+		return 1;
+	}
+	
+	iteration = 0;
+	
+	for (;;)
+	{
+		if (iteration++ > 0x1000) return 0;
+		
+		error = moonfish_step(argv[0], file, gradient);
+		
+		printf("\n");
+		for (i = 0 ; i < moonfish_size ; i++)
+			printf("%.0f,", moonfish_values[i]);
+		printf("\n");
+		
+		printf("iteration: %d\n", iteration);
+		printf("current error: ");
+		if (error > 10000 * 1000)
+			printf("%.0fM\n", error / 1000 / 1000);
+		else if (error > 10000)
+			printf("%.0fK\n", error / 1000);
+		else
+			printf("%.0f\n", error);
+	}
+}
--- a/tools/lichess.c
+++ b/tools/lichess.c
@@ -400,19 +400,19 @@
 			invalid = moonfish_from_fen(&chess, fen->valuestring);
 			
 			if (!invalid)
-			if (chess.white_oo || chess.white_ooo)
+			if (chess.oo[0] || chess.ooo[0])
 			if (chess.board[25] != moonfish_white_king)
 				invalid = 1;
 			
 			if (!invalid)
-			if (chess.black_oo || chess.black_ooo)
+			if (chess.oo[1] || chess.ooo[1])
 			if (chess.board[95] != moonfish_black_king)
 				invalid = 1;
 			
-			if (!invalid && chess.white_ooo && chess.board[21] != moonfish_white_rook) invalid = 1;
-			if (!invalid && chess.white_oo && chess.board[28] != moonfish_white_rook) invalid = 1;
-			if (!invalid && chess.black_ooo && chess.board[91] != moonfish_black_rook) invalid = 1;
-			if (!invalid && chess.black_oo && chess.board[98] != moonfish_black_rook) invalid = 1;
+			if (!invalid && chess.oo[0] && chess.board[28] != moonfish_white_rook) invalid = 1;
+			if (!invalid && chess.oo[1] && chess.board[98] != moonfish_black_rook) invalid = 1;
+			if (!invalid && chess.ooo[0] && chess.board[21] != moonfish_white_rook) invalid = 1;
+			if (!invalid && chess.ooo[1] && chess.board[91] != moonfish_black_rook) invalid = 1;
 			
 			if (invalid)
 			{
--