home / fornax / fornax-v4-0 / src / supplementary / testsuite.cpp

testsuite.cpp



//
//  testsuite.cpp
//  fornax3
//
//  Created by Anders on 24/12/2020.
//

#include <stdio.h>
#include <stdarg.h>
#include <assert.h>
#include "testsuite.hpp"
#include "perft.hpp"
#include "../board.h"
#include "../libs/stopwatch.h"
#include "../parsing.hpp"
#include "../search/search.hpp"
#include "../eval/eval.hpp"

static long fail;
static long success;
static long total;
static long maxdepth = 100;
static int eval_total_distance;

static void test_search(const char* name, const char* should, const char* fen, uint8_t depth);

static void search_suite_1_solids() {
  test_search("QGA-1", "a4b5", "rnbqkbnr/p3pppp/2p5/1p6/P1pP4/4P3/1P3PPP/RNBQKBNR w KQkq - 0 5", 9);
  test_search("QGA-2", "d1f3", "rnbqkbnr/p3pppp/8/1p6/2pP4/4P3/1P3PPP/RNBQKBNR w KQkq - 0 6", 9);
  test_search("WAC.001", "g3g6", "2rr3k/pp3pp1/1nnqbN1p/3pN3/2pP4/2P3Q1/PPB4P/R4RK1 w - - 0 1", 9);
  test_search("WAC.003", "e3g3", "5rk1/1ppb3p/p1pb4/6q1/3P1p1r/2P1R2P/PP1BQ1P1/5RKN w - - 0 1", 9);
  test_search("WAC.004", "h6h7", " r1bq2rk/pp3pbp/2p1p1pQ/7P/3P4/2PB1N2/PP3PPR/2KR4 w - - ", 9);
  test_search("UP-1", "g2h1n", "3rQB1k/p5b1/2b4p/8/8/1P6/P4KpP/2R2B1R b - - 0 30", 9);
  test_search("UP-2", "b2b1n", "8/8/8/8/8/2K5/1p4R1/2k5 b - - 3 77", 9);
  test_search("EP-1", "e4f3", "r3k2r/pbppqpb1/1pn3p1/7p/1N2pPn1/1PP4N/PB1P2PP/2QRKR2 b kq f3 0 1", 9);
  test_search("EP-2", "e5f6", "r2q1r2/pbpn2p1/1pn1p1k1/4PpN1/3P4/2PQ4/P4PPP/R1B1R1K1 w - f6 0 15", 9);
  test_search("CAST-1", "e8c8", "r3k1K1/2pq4/3p4/p2Pp1b1/1pP1Pp2/1B3PP1/PP1N4/RNBQ1R2 b q - 0 8", 9);
  test_search("CAST-2", "e1c1", "r7/p4ppp/B2kpn2/8/8/2P2P2/Pr3P1P/R3K2R w KQ - 0 18", 9);
  test_search("DEF-2", "c6f3", "2q2rk1/2p3pp/N1b5/p7/1r3nP1/1PQR1P2/1BP5/3K2R1 b - -", 9);
  test_search("STALE-1", "e2b2", "7k/R7/R3p1Q1/4p3/p3B3/P7/KP2r2q/2B5 b - - 0 1", 10);
  test_search("MATE#5", "d1e1", "4r3/2R2P1p/1p4pk/p7/5P2/8/1Q3KPP/3q4 b - - 1 35", 10);
  test_search("FINE#70", "a1b1", "8/k7/3p4/p2P1p2/P2P1P2/8/8/K7 w - -", 27);
  test_search("Sackmann", "e5f5", "8/8/2p/k1p1K/p1P/P/8/8 w - -", 27);
  test_search("Reti", "h8g7", "7K/8/k1P5/7p/8/8/8/8 w - - 0 1", 17);
  test_search("HAKMEM70", "g7g8n", "5B2/6P1/1p6/8/1N6/kP6/2K5/8 w - -", 9);
  test_search("Saavedra", "c6c7", "8/8/1KP5/3r4/8/8/8/k7 w - - 0 1", 11);
  test_search("BK.01", "d6d1", "1k1r4/pp1b1R2/3q2pp/4p3/2B5/4Q3/PPP2B2/2K5 b - -", 9);
  test_search("BK.12", "d7f5", "r3r1k1/ppqb1ppp/8/4p1NQ/8/2P5/PP3PPP/R3R1K1 b - -", 9);
  test_search("BK.14", "d1d2", "rnb2r1k/pp2p2p/2pp2p1/q2P1p2/8/1Pb2NP1/PB2PPBP/R2Q1RK1 w - -", 9);
  test_search("BK.15", "g4g7", "2r3k1/1p2q1pp/2b1pr2/p1pp4/6Q1/1P1PP1R1/P1PN2PP/5RK1 w - -", 9);
  test_search("BK.16", "d2e4", "r1bqkb1r/4npp1/p1p4p/1p1pP1B1/8/1B6/PPPN1PPP/R2Q1RK1 w kq -", 9);
  test_search("BK.21", "f5h6", "3rn2k/ppb2rpp/2ppqp2/5N2/2P1P3/1P5Q/PB3PPP/3RR1K1 w - -", 9);
}


static void search_suite_2_bkfull() {
  test_search("BK.01", "d6d1", "1k1r4/pp1b1R2/3q2pp/4p3/2B5/4Q3/PPP2B2/2K5 b - -", 11);
  test_search("BK.02", "d4d5", "3r1k2/4npp1/1ppr3p/p6P/P2PPPP1/1NR5/5K2/2R5 w - -", 13);
  test_search("BK.03", "f6f5", "2q1rr1k/3bbnnp/p2p1pp1/2pPp3/PpP1P1P1/1P2BNNP/2BQ1PRK/7R b - -", 13);
  test_search("BK.04", "e5e6", "rnbqkb1r/p3pppp/1p6/2ppP3/3N4/2P5/PPP1QPPP/R1B1KB1R w KQkq -", 11);
  test_search("BK.05", "c3d5", "r1b2rk1/2q1b1pp/p2ppn2/1p6/3QP3/1BN1B3/PPP3PP/R4RK1 w - -", 13);
  test_search("BK.06", "g5g6", "2r3k1/pppR1pp1/4p3/4P1P1/5P2/1P4K1/P1P5/8 w - -", 15);
  test_search("BK.07", "h5f6", "1nk1r1r1/pp2n1pp/4p3/q2pPp1N/b1pP1P2/B1P2R2/2P1B1PP/R2Q2K1 w - -", 13);
  test_search("BK.08", "f4f5", "4b3/p3kp2/6p1/3pP2p/2pP1P2/4K1P1/P3N2P/8 w - -", 15);
  test_search("BK.09", "f4f5", "2kr1bnr/pbpq4/2n1pp2/3p3p/3P1P1B/2N2N1Q/PPP3PP/2KR1B1R w - -", 11);
  test_search("BK.10", "c6e5", "3rr1k1/pp3pp1/1qn2np1/8/3p4/PP1R1P2/2P1NQPP/R1B3K1 b - -", 13);
  test_search("BK.11", "f2f4", "2r1nrk1/p2q1ppp/bp1p4/n1pPp3/P1P1P3/2PBB1N1/4QPPP/R4RK1 w - -", 11);
  test_search("BK.12", "d7f5", "r3r1k1/ppqb1ppp/8/4p1NQ/8/2P5/PP3PPP/R3R1K1 b - -", 11);
  test_search("BK.13", "b2b4", "r2q1rk1/4bppp/p2p4/2pP4/3pP3/3Q4/PP1B1PPP/R3R1K1 w - -", 11);
  test_search("BK.14", "d1d2", "rnb2r1k/pp2p2p/2pp2p1/q2P1p2/8/1Pb2NP1/PB2PPBP/R2Q1RK1 w - -", 11);
  test_search("BK.15", "g4g7", "2r3k1/1p2q1pp/2b1pr2/p1pp4/6Q1/1P1PP1R1/P1PN2PP/5RK1 w - -", 11);
  test_search("BK.16", "d2e4", "r1bqkb1r/4npp1/p1p4p/1p1pP1B1/8/1B6/PPPN1PPP/R2Q1RK1 w kq -", 11);
  test_search("BK.17", "h7h5", "r2q1rk1/1ppnbppp/p2p1nb1/3Pp3/2P1P1P1/2N2N1P/PPB1QP2/R1B2RK1 b - -", 11);
  test_search("BK.18", "c5b3", "r1bq1rk1/pp2ppbp/2np2p1/2n5/P3PP2/N1P2N2/1PB3PP/R1B1QRK1 b - -", 11);
  test_search("BK.19", "e8e4", "3rr3/2pq2pk/p2p1pnp/8/2QBPP2/1P6/P5PP/4RRK1 b - -", 11);
  test_search("BK.20", "g3g4", "r4k2/pb2bp1r/1p1qp2p/3pNp2/3P1P2/2N3P1/PPP1Q2P/2KRR3 w - -", 11);
  test_search("BK.21", "f5h6", "3rn2k/ppb2rpp/2ppqp2/5N2/2P1P3/1P5Q/PB3PPP/3RR1K1 w - -", 11);
  test_search("BK.22", "b7e4", "2r2rk1/1bqnbpp1/1p1ppn1p/pP6/N1P1P3/P2B1N1P/1B2QPP1/R2R2K1 b - -", 13);
  test_search("BK.23", "f7f6", "r1bqk2r/pp2bppp/2p5/3pP3/P2Q1P2/2N1B3/1PP3PP/R4RK1 b kq -", 11);
  test_search("BK.24", "f2f4", "r2qnrnk/p2b2b1/1p1p2pp/2pPpp2/1PP1P3/PRNBB3/3QNPPP/5RK1 w - -", 11);
}


static void search_suite_3_win_at_chess() {
  test_search("WAC.001", "g3g6", "2rr3k/pp3pp1/1nnqbN1p/3pN3/2pP4/2P3Q1/PPB4P/R4RK1 w - - 0 1", 11);
  test_search("WAC.002", "b3b2", "8/7p/5k2/5p2/p1p2P2/Pr1pPK2/1P1R3P/8 b - -", 18);
  test_search("WAC.003", "e3g3", "5rk1/1ppb3p/p1pb4/6q1/3P1p1r/2P1R2P/PP1BQ1P1/5RKN w - - 0 1", 11);
  test_search("WAC.004", "h6h7", "r1bq2rk/pp3pbp/2p1p1pQ/7P/3P4/2PB1N2/PP3PPR/2KR4 w - - ", 11);
  test_search("WAC.005", "c6c4", "5k2/6pp/p1qN4/1p1p4/3P4/2PKP2Q/PP3r2/3R4 b - -", 11);
  test_search("WAC.006", "b6b7", "7k/p7/1R5K/6r1/6p1/6P1/8/8 w - -", 11);
  test_search("WAC.007", "g4e3", "rnbqkb1r/pppp1ppp/8/4P3/6n1/7P/PPPNPPP1/R1BQKBNR b KQkq -", 11);
  test_search("WAC.008", "e7f7", "r4q1k/p2bR1rp/2p2Q1N/5p2/5p2/2P5/PP3PPP/R5K1 w - -", 11);
  test_search("WAC.009", "d6h2", "3q1rk1/p4pp1/2pb3p/3p4/6Pr/1PNQ4/P1PB1PP1/4RRK1 b - -", 11);
  test_search("WAC.010", "h4h7", "2br2k1/2q3rn/p2NppQ1/2p1P3/Pp5R/4P3/1P3PPP/3R2K1 w - -", 11);
  test_search("WAC.011", "f3c6", "r1b1kb1r/3q1ppp/pBp1pn2/8/Np3P2/5B2/PPP3PP/R2Q1RK1 w kq -", 11);
  test_search("WAC.012", "g4f3", "4k1r1/2p3r1/1pR1p3/3pP2p/3P2qP/P4N2/1PQ4P/5R1K b - -", 11);
  test_search("WAC.013", "f1f8", "5rk1/pp4p1/2n1p2p/2Npq3/2p5/6P1/P3P1BP/R4Q1K w - -", 11);
  test_search("WAC.014", "h3h7", "r2rb1k1/pp1q1p1p/2n1p1p1/2bp4/5P2/PP1BPR1Q/1BPN2PP/R5K1 w - -", 11);
  test_search("WAC.015", "b8b7", "1R6/1brk2p1/4p2p/p1P1Pp2/P7/6P1/1P4P1/2R3K1 w - -", 11);
  test_search("WAC.016", "e2c3", "r4rk1/ppp2ppp/2n5/2bqp3/8/P2PB3/1PP1NPPP/R2Q1RK1 w - -", 11);
  test_search("WAC.017", "c4e5", "1k5r/pppbn1pp/4q1r1/1P3p2/2NPp3/1QP5/P4PPP/R1B1R1K1 w - -", 11);
  test_search("WAC.018", "a8h8", "R7/P4k2/8/8/8/8/r7/6K1 w - -", 23);
  test_search("WAC.019", "c5c6", "r1b2rk1/ppbn1ppp/4p3/1QP4q/3P4/N4N2/5PPP/R1B2RK1 w - -", 11);
  test_search("WAC.020", "d7b5", "r2qkb1r/1ppb1ppp/p7/4p3/P1Q1P3/2P5/5PPP/R1B2KNR b kq -", 11);
}

static void search_suite_4_fornax_games() {
  
  test_search("FG1", "g4f2", "7k/2p5/rp6/5p1p/3RbQnP/6PK/2qB4/3R4 b - - 4 38", 9);
  test_search("FG2", "d4c4", "5q1k/8/1PQp2p1/1p2p2p/3r3P/5BP1/3P4/4K3 b - - 0 40", 11);
  test_search("FG3", "e8d7", "r1bqk2r/pp2b2p/2np4/2pQpp2/2P1P3/5N2/PP2BPPP/R1B2RK1 b kq - 1 11", 10);
  test_search("FG4", "e2f3", "8/r4q1k/2P1QP1b/3p1P2/p7/6RP/P1r1K1B1/5R2 w - - 3 38", 13);
  test_search("FG5", "e4f5", "rn3rk1/pp3ppp/1qp5/3pN3/4B3/2P3P1/PPP2P1P/R1BQ1bK1 w - - 0 13", 13);
  test_search("WAC.002", "b3b2", "8/7p/5k2/5p2/p1p2P2/Pr1pPK2/1P1R3P/8 b - -", 21);
  test_search("SAC-1", "d3h7", "3rr1k1/1pqb1ppp/p7/2bp2P1/3B1P2/P2B1Q2/1PP4P/4RR1K w - - -", 14);
  test_search("Behting", "d5c6", "8/8/7p/3KNN1k/2p4p/8/3P2p1/8 w - - ", 21);
  
}


void search_suite_standard(void) {
  search_suite_1_solids();
}

void testsuite_search(int n) {
  fail = 0;
  success = 0;
  total = 0;
  long start = stopwatch_wall_start();
  switch (n) {
    case 1:
      search_suite_1_solids();
      break;
    case 2:
      search_suite_2_bkfull();
      break;
    case 3:
      search_suite_3_win_at_chess();
      break;
    case 4:
      search_suite_4_fornax_games();
      break;
    default:
      break;
  }
  stopwatch_wall_stop_print(start);
  assert(fail + success == total);
  printf("SEARCH TESTS %ld/%ld \n", success, total);
}


static void test_search(const char* name, const char* should, const char* fen, uint8_t depth) {
  Board b = parsing_from_fen(fen);
  printf("\ntesting %s ...\n", name);
  parsing_printboard(&b);
  printf("\n");
  
  move s = parsing_move_from_uci(should);
  move was = search_iterative(&b, depth);
  
  if (s != was) {
    printf("SEARCH FAIL: '%s' depth=%d (should ", name, depth);
    parsing_printmove(s);
    printf(" was ");
    parsing_printmove(was);
    printf(") \n");
    fail++;
  }
  else {
    success++;
  }
  total++;
}

static void test_perft(const char* fen, const char* name, ...) {
  Board board = parsing_from_fen(fen);
  
  long start = stopwatch_wall_start();
  
  
  printf("perft '%s' %s \n", name, fen);
  
  int depth = 0;
  long should = 0;
  va_list arglist;
  va_start(arglist, name);
  while ((should = va_arg(arglist, long)) > 0) {
    
    if (depth >= maxdepth) break;
    
    printf("  (%d) %ld / ", ++depth, should);
    long was = perft(&board, depth, false);
    total++;
    if (should != was) {
      printf("%ld fail!\n", was);
      fail++;
    }
    else {
      printf("%ld ok\n", was);
      success++;
    }
  }
  
  printf("-> time = %.3fs\n", (double) stopwatch_wall_stop(start) / 1000);
  
  va_end( arglist );
}

static void test_perft(const char* fen, const char* name, ...);
static void perft_suite1() {
  printf("running perft suite 1 ...\n");
  test_perft("8/8/8/8/8/8/8/KNknNnNn w - -", "king and knights",
                 11L, 97L, 1223L, 12956L, 172980L, 2131736L, 30372864L, 400749102L, 0L);
  test_perft("8/4k3/5b2/6b1/7B/8/K7/3B4 b - -", "king and bishops",
                 20L, 281L, 5264L, 83927L, 1643419L, 28323482L, 555907709L, 0L);
  test_perft("3r4/8/4Rrk1/8/8/2K5/8/5R2 b - -", "king and rooks",
                 21L, 560L, 12318L, 321441L, 7574907L, 194127344L, 0L);
  test_perft("8/8/2q1k3/8/3Q4/3K4/8/8 w - -", "king and queens",
                 27L, 488L, 9364L, 186469L, 3613112L, 71282116L, 0L, 1399815012L, 0L);
  test_perft("3b4/7b/bN3k2/1N3B2/2N5/2K5/b3N3/5b1b w - -", "knight bishop mix",
                 39L, 1073L, 37230L, 1133543L, 36427345L, 0L, 1172612788L, 0L);
  test_perft("rrN1n1b1/8/1bk5/2Bn4/3K1n2/8/8/RR2N2B w - -", "knight bishop rook mix",
                 30L, 800L, 23877L, 695332L, 21871950L, 677864932L, 0L);
  test_perft("7N/4b1k1/2b2q2/n6n/4B3/1RR5/1K4N1/B5Q1 w - -", "knight bishop rook queen mix",
                 38L, 1311L, 50378L, 1873719L, 76806199L, 0L, 2841973739L, 0L);
  test_perft("8/p4p2/pk6/2p2PP1/2P3p1/2p5/3PK2P/8 w - -", "king and pawns",
                 13L, 116L, 1324L, 12971L, 136902L, 1435651L, 14445736L, 161693685L, 0L, 1584938933L, 0L);
  test_perft("8/8/8/2k5/2pP4/4K3/8/8 b - d3", "pinned pawns",
                 7L, 46L, 333L, 2615L, 19301L, 144498L, 1070695L, 7970196L, 0L);
  test_perft("8/8/8/3k4/2p1pP2/1Q1N4/1K4B1/8 b - f3", "pinned pawns 2",
                 6L, 171L, 1197L, 33411L, 229529L, 6543487L, 45516045L, 0L, 1322862736L, 0L);
  test_perft("1q2b3/k1P1P1P1/8/8/P7/8/1PK5/8 w - - 0 1", "many promotions",
                 21L, 371L, 6868L, 143159L, 2885103L, 65006419L, 0L, 1410501175L, 33391838989L, 0L);
  test_perft("7N/2P1b1k1/P1b2q2/n3p2n/2p1B3/1RP4p/1K2PpN1/B5Q1 w - -", "supermix",
                 39L, 1537L, 57922L, 2414933L, 89885954L, 0L, 3802528747L, 0L);
  test_perft("2kr3r/1p1bbp2/p1pn1qpp/Q1Pp3P/N5P1/5BB1/PP1RPP2/2K4R w - -", "slav mix",
                 40L, 1263L, 48539L, 1564456L, 60833544L, 0L, 2010391889L, 0L);
  test_perft("r3k2r/1ppb1ppp/p1n1pq2/3p4/P1PPn3/3BPN2/PB1Q1PPP/R3K2R w KQkq - 2 12", "castling opportunities",
                 37L, 1724L, 63031L, 2831006L, 102716627L, 0L, 4479776186L, 0L);
  test_perft("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq -", "startpos",
                 20L, 400L, 8902L, 197281L, 4865609L, 0L);
  test_perft("rnbqkbnr/pppp1ppp/8/4p3/4P3/8/PPPP1PPP/RNBQKBNR w KQkq e6", "e2e4 e7e5",
                 29L, 835L, 24825L, 728887L, 22273312L, 0L);
  test_perft("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq -", "kiwi",
                 48L, 2039L, 97862L, 4085603L, 193690690L, 0L, 8031647685L, 0L);
  test_perft("8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - -", "enpassant pinned",
                 14L, 191L, 2812L, 43238L, 674624L, 11030083L, 178633661L, 0L);
  test_perft("8/6bb/8/8/R1pP2k1/4P3/P7/K7 b - d3", "enpassant pinned 2",
                 21L, 206L, 4135L, 53486,1045344L, 14963066L, 0L);
  test_perft("8/2p5/3p4/KP5r/1R2p2k/5P2/3P4/8 w - - 0 1", "enpassant pinned 3",
                 13L, 193L, 2577L, 42483L, 613076L, 10609291L, 0L);
  test_perft("2r3k1/1q1nbppp/r3p3/3pP3/pPpP4/P1Q2N2/2RN1PPP/2R4K b - b3 0 23", "enpassant 2",
                 46L, 1356L, 56661L, 1803336,72293225L, 0L);
  test_perft("r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1", "carnage",
                 6L, 264L, 9467L, 422333L, 15833292L, 0L);
  test_perft("r2q1rk1/pP1p2pp/Q4n2/bbp1p3/Np6/1B3NBn/pPPP1PPP/R3K2R b KQ - 0 1", "carnage inversed",
                 6L, 264L, 9467L, 422333L, 15833292L, 0L);
  test_perft("rnbq1k1r/pp1Pbppp/2p5/8/2B5/8/PPP1NnPP/RNBQK2R w KQ - 1 8", "bugsy",
                 44L, 1486L, 62379L, 2103487L, 89941194L, 0L);
  test_perft("r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10", "altsy",
                 46L, 2079L, 89890L, 3894594L, 164075551L, 0L);
  test_perft("rnbqkbnr/pPppppp1/8/8/8/8/1PPPPPpP/RNBQKBNR w KQkq - 0 5", "promo",
                 31L, 957L, 28885L, 919300L, 29394458L, 0L);
  test_perft("8/2k1r3/3r4/8/8/1R6/2K3R1/8 w - -", "king and rooks 2",
                 29L, 792L, 21085L, 558207L, 14687292L, 0L);
  test_perft("8/2k5/b6b/8/8/1B6/2K5/6B1 w - -", "king and bishops 2",
                 18L, 337L, 6620L, 123940L, 2470048L, 46688664L, 0L);
  test_perft("8/2k1nn2/8/8/8/8/2K2NN1/8 w - -", "king and knights 2",
                 18L, 359L, 6176L, 114053L, 1911342L, 0L);
  test_perft("8/2k2q2/3q4/8/8/8/1QK3Q1/8 w - -", "king and queens 2",
                 40L, 1478L, 46412L, 1572567L, 0L);
  test_perft("8/p1k4p/8/8/8/8/2K1P1P1/8 w - -", "king and pawns 2",
                 12L, 144L, 1535L, 16290L, 162078L, 1611733L, 15412327L, 0L);
  test_perft("5nkb/5p1p/Q2B2p1/8/P7/5BKP/6P1/6q1 w - - 4 33", "xray",
                 35L, 1024L, 31389L, 934526L, 28882969L, 0L);
}

void testsuite_perft(void) {
  fail = 0;
  success = 0;
  total = 0;
  long start = stopwatch_wall_start();
  perft_suite1();
  printf("-> total time = %.3fs\n", (double) stopwatch_wall_stop(start) / 1000);
  assert(fail + success == total);
  if (success == total) {
    printf("all test ok %ld/%ld \n", success, total);
  }
  else {
    printf("some tests failed %ld/%ld !! \n", success, total);
  }
}

static void test_eval(const char* label, const char* fen, eval min, eval max) {
  Board b = parsing_from_fen(fen);
  
  printf("testing %s:\n", label);
  parsing_printfen(&b);
  printf("\n");
  
  
  eval ev = eval_evaluate(&b);
  total++;
  
  eval mid = (max - min) / 2 + min;
  eval dist = (eval) abs(mid - ev);
  eval_total_distance += dist;
         
  printf("eval =  %d (<%d, %d>)\n", ev, min, max);
  
    if (ev < min) {
    fail++;
    printf("FAIL: eval = %d below expected bounds = %d\n", ev, min);
  }
  else if (ev > max) {
    fail++;
    printf("FAIL: eval = %d above expected bounds = %d\n", ev, max);
  }
  else {
    success++;
  }
  
  printf("----------------------------------------------\n");
}


void testuite_eval(void) {
  fail = 0;
  success = 0;
  total = 0;
  eval_total_distance = 0;
  test_eval("startpos", "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", 0, 0);
  test_eval("pos1", "r1bqkb1r/ppp2ppp/2n1pn2/8/2PPp3/2N1B3/PP3PPP/R2QKBNR w KQkq - 2 6", -150, -10);
  test_eval("pos2", "r7/ppk3p1/2pq1n1p/3p1B2/3P4/2N5/PPP2PPP/1R2R1K1 w - - 0 23", -150, -5);
  test_eval("rd1-lagrave-caruana", "1rbqk2r/b1p2ppp/p1np1n2/Pp2p3/3PP3/1BP2N2/1P3PPP/RNBQ1RK1 w k - 1 11", 60, 200);
  test_eval("rd1-ding-hao", "rnbqr1k1/ppb2ppp/2pp1n2/4p3/NPP5/P2P1NP1/4PPBP/R1BQ1RK1 w - - 1 11", 0, 50);
  test_eval("rd1-giri-nepomniachtchi", "r1b1kb1r/1p1p1ppp/1q2p3/1p2n3/2P2Bn1/2N1P1PP/PP3P2/R2QKB1R w KQkq - 0 11", -550, 50);
  test_eval("rd1-grischuk-alekseenko", "1rbq1rk1/ppp2pp1/2n4p/2b1p3/8/2PP1NP1/P1Q1PPBP/R1B2RK1 w - - 2 11", -50, 50);
  test_eval("rd2-caruana-alekseenko", "rnbqr1k1/pp2bppp/5n2/2pP4/2P5/P1N5/1P2B1PP/R1BQK1NR w KQ - 3 11", -40, 40);
  test_eval("rd2-nepomniachtchi-grischuk", "r1b1kb1r/ppp2pp1/2p5/4Pn1p/8/2N2N1P/PPP2PP1/R1B2RK1 w - - 0 11", 20, 100);
  test_eval("rd2-hao-giri", "r1bqk2r/pp3pb1/2n2npp/1N1p4/3p3P/5NP1/PP2PPB1/R1BQ1RK1 w kq - 2 11", -50, 50);
  test_eval("rd2-lagrave-ding", "r4rk1/1bpqbppp/p1np1n2/1p2p3/4P3/PB1P1N1P/1PP2PP1/RNBQR1K1 w - - 1 11", -50, 50);
  test_eval("rd3-liren-caruana", "rn1q1rk1/pp3ppp/2p2n2/4N3/Pb1P4/2N2P2/1Pb1PKPP/R1BQ1B1R w - - 1 11", -30, 100);
  test_eval("rd3-giri-lagrave", "rnb2rk1/pp2pp1p/3q2p1/2pP4/4P3/2P5/P2Q1PPP/R3KBNR w KQ - 3 11", 10, 100);
  test_eval("rd3-grischuk-hao", "rn2k2r/ppp1q1pp/3bpn2/3p4/3P4/3B4/PPP1QPPP/RNB1K2R w KQkq - 0 11", -5, 100);
  test_eval("rd3-alekseenko-nepomniachtchi", "rn2k2r/p1q1nppp/1p2p3/2ppP3/b2P3P/P1P2N2/2P1BPP1/R1BQK2R w KQkq - 4 11", -5, 60);
  test_eval("king-safety-1", "7r/1p3p1k/2n1p2p/5p2/1p6/2q1PNQ1/r1P2PPP/2KR1B1R w - - 5 21", -1000, -200);
  test_eval("king-safety-2", "1r4k1/2p1q1p1/2p2p1p/3p3P/p2PnB1R/4PQ2/P1P2PP1/2K5 w - - - -", -600, -100);
  test_eval("king-safety-3", "3r2k1/pp3pp1/2pb1q1p/P2p1b2/3P4/2N1B2r/1PPQBPR1/1R4K1 w - - 2 26", -200, 0);
  test_eval("king-safety-4", "3r2k1/R4Rp1/1p6/1P6/1NK1b3/7r/1P6/8 b - - - -", 0, 200);
  test_eval("king-safety-counter-1", "r2q2k1/5ppp/p2br3/1b5Q/3B1P2/2PR4/P1P3PP/5RK1 w - - 0 1", -400, -100);
  test_eval("king-safety-counter-2", "rn1q1r1k/pp2nppp/2p1b3/3pp2Q/1b2P3/1BN2N2/PPPP1PPP/R1B2RK1 b - - 7 10", -100, 20);
  test_eval("FG1", "1r4k1/4p1p1/4p2p/n7/N2pP2K/P5P1/1PR3P1/8 w - - 0 1", -100, 100);
  test_eval("FG2", "r3kb1r/p2b1ppp/2p2n2/2p1N3/8/1PB1PP2/P1PP1P1P/R3K2R w KQkq - - -", -250, -100);
  test_eval("FG3", "6k1/p3rp1p/2P5/3K4/8/5BP1/4P3/8 b - - 2 47", 50, 150);
  test_eval("FG4", "4r3/1b4r1/1p1kp1p1/1P1pR2p/1K1P1P1P/3BP3/8/R7 w - -", 75, 200);
  test_eval("FG5 king safety", "2r1nrk1/pppqbpp1/2np3P/8/3PPN2/P3B3/1PP4Q/2K3RR b - - 0 24", -300, 25);
  test_eval("passer", "r5k1/P2R3p/2p5/6p1/2r5/4B2P/5PK1/8 b - - 0 31", -100, 0);
  test_eval("backwards", "1r3rk1/p2p1ppp/b1nN1n2/1p2p3/1P2P1P1/P4P2/B1PR1B1P/2KR4 b - - 0 1", -300, -75);
  
  test_eval("EG K v kn", "8/8/5K2/8/1n6/5k2/8/8 w - - 0 1", -50, 0);
  test_eval("EG KN v k", "8/8/5K2/1N6/8/5k2/8/8 w - - 0 1", 0, 50);
  test_eval("EG K v kb", "8/8/5K2/1b6/8/5k2/8/8 w - - 0 1", -60, 0);
  test_eval("EG KB v k", "8/8/5K2/8/1B6/5k2/8/8 w - - 0 1", 0, 60);
  test_eval("EG K v knn", "8/8/1n3K2/1n6/8/5k2/8/8 w - - 0 1", -100, 0);
  test_eval("EG KNN v k", "8/8/5K2/8/1N6/1N3k2/8/8 w - - 0 1", 0, 100);
  test_eval("EG KN v kr", "8/8/1N3K2/8/8/2r2k2/8/8 w - - 0 1", -100, 0);
  test_eval("EG KR v kn", "8/8/2R2K2/8/8/1n3k2/8/8 w - - 0 1", 0, 100);
  test_eval("EG KR v krn", "8/8/3R1K2/8/8/1nr2k2/8/8 w - - 0 1", -100, 0);
  test_eval("EG KRN v kr", "8/8/1NR2K2/8/8/3r1k2/8/8 w - - 0 1", 0, 100);
  test_eval("EG KN v kbn", "8/8/2N2K2/8/8/1bn2k2/8/8 w - - 0 1", -100, 0);
  test_eval("EG KBN v kn", "8/8/1BN2K2/8/8/2n2k2/8/8 w - - 0 1", 0, 100);
  test_eval("EG KB v kbn", "8/8/1B3K2/8/8/1bn2k2/8/8 w - - 0 1", -100, 0);
  test_eval("EG KBN v kb", "8/8/1BN2K2/8/8/1b3k2/8/8 w - - 0 1", 0, 100);
  test_eval("EG KN v kbb", "8/8/2N2K2/8/8/bb3k2/8/8 w - - 0 1", -100, 0);
  test_eval("EG KBB v kn", "8/8/BB3K2/8/8/2n2k2/8/8 w - - 0 1", 0, 100);
  test_eval("EG Kb v kbb", "8/8/1B3K2/8/8/bb3k2/8/8 w - - 0 1", -100, 0);
  test_eval("EG KBB v kb", "8/8/BB3K2/8/8/1b3k2/8/8 w - - 0 1", 0, 100);
  test_eval("EG KB v knn", "8/8/1B3K2/8/8/1nn2k2/8/8 w - - 0 1", -100, 0);
  test_eval("EG KNN v kb", "8/8/1NN2K2/8/8/1b3k2/8/8 w - - 0 1", 0, 100);
  test_eval("EG KN v knn", "8/8/1N3K2/8/8/1nn2k2/8/8 w - - 0 1", -100, 0);
  test_eval("EG KNN v kn", "8/8/1NN2K2/8/8/1n3k2/8/8 w - - 0 1", 0, 100);
  test_eval("EG KP v kn", "8/8/1P3K2/8/8/1n3k2/8/8 w - - 0 1", 0, 50);
  test_eval("EG KN v kp", "8/8/1N3K2/8/8/1p3k2/8/8 w - - 0 1", -50, 0);
  test_eval("EG KP v kb", "8/8/1P3K2/8/8/1b3k2/8/8 w - - 0 1", 0, 50);
  test_eval("EG KB v kp", "8/8/1B3K2/8/8/1p3k2/8/8 w - - 0 1", -50, 0);
  test_eval("EG KP v knn", "8/8/1P3K2/8/8/1nn2k2/8/8 w - - 0 1", -200, 0);
  test_eval("EG KNN v kp", "8/8/1NN2K2/8/8/1p3k2/8/8 w - - 0 1", 0, 200);
  test_eval("EG K v kp", "8/8/2p2k2/8/8/5K2/8/8 w - - 0 1", -120, 0);
  test_eval("EG KP v k", "8/8/5k2/8/8/2P2K2/8/8 w - - 0 1", 0, 120);
  test_eval("EG KN v kpn", "8/8/2pn1k2/8/8/3N1K2/8/8 w - - 0 1", -80, 0);
  test_eval("EG KPN v kn", "8/8/3n1k2/8/8/2PN1K2/8/8 w - - 0 1", 0, 80);
  test_eval("EG KB v kpn", "8/8/2pn1k2/8/8/3B1K2/8/8 w - - 0 1", -80, 0);
  test_eval("EG KPN v kb", "8/8/3b1k2/8/8/2PN1K2/8/8 w - - 0 1", 0, 80);
  test_eval("EG KB v kpb", "8/8/2pb1k2/8/8/3B1K2/8/8 w - - 0 1", -80, 0);
  test_eval("EG KPB v kb", "8/8/3b1k2/8/8/2PB1K2/8/8 w - - 0 1", 0, 80);
  test_eval("EG KN v kpb", "8/8/2pb1k2/8/8/3N1K2/8/8 w - - 0 1", -100, 0);
  test_eval("EG KPB v kn", "8/8/3n1k2/8/8/2PB1K2/8/8 w - - 0 1", 0, 100);
  
  test_eval("EG K v kbn", "8/8/3nbk2/8/8/5K2/8/8 w - - 0 1", -800, -500);
  test_eval("EG KNB v k", "8/8/5k2/8/8/3NBK2/8/8 w - - 0 1", 500, 800);
  test_eval("EG K v kr", "8/8/4rk2/8/8/5K2/8/8 w - - 0 1", -600, -400);
  test_eval("EG KR v k", "8/8/5k2/8/8/4RK2/8/8 w - - 0 1", 400, 600);
  test_eval("EG KP v kbp", "8/8/2p1bk2/8/8/2P2K2/8/8 w - - 0 1", -500, -300);
  test_eval("EG KPB v kp", "8/8/2p2k2/8/8/2P1BK2/8/8 w - - 0 1", 300, 500);
  
  assert(fail + success == total);
  printf("EVAL TESTS %ld/%ld (total dist = %d) \n", success, total, eval_total_distance);
}