Chess engine in zig
const std = @import("std");

const Chess = @import("Chess.zig");
const BitMove = @import("Board.zig").BitMove;
const BitMoveType = @import("Board.zig").BitMoveType;
const Search = @import("Search.zig").Search;
const Square = @import("Board.zig").Square;
const MoveList = @import("Board.zig").MoveList;

// These values are arbitrary, but only decreasing makes sense.
const CAPTURE_BOOST = 10_000;
const KILLER_FIRST = 9_000;
const KILLER_SECOND = 8_000;

// PV value score
pub const PV_SCORE = 20_000;

// Best move score
const BEST_MOVE_SCORE = 30_000;

// Killer moves [first/second][MAX_PLY]
pub var killer_moves: [2][64]BitMove = undefined;

// History moves [piece][square]
pub var history_moves: [@typeInfo(Chess.PE).Enum.fields.len - 1][@typeInfo(Square).Enum.fields.len]usize = undefined;

pub fn moves(search: *const Search, ml: *MoveList, best: BitMove) void {
    for (ml.slice()) |*mp| {
        mp.score = scoreMove(search, mp.move, best);
    }
}

fn scoreMove(search: *const Search, move: BitMove, best: BitMove) isize {
    if (@as(BitMoveType, @bitCast(move)) == @as(BitMoveType, @bitCast(best))) {
        return BEST_MOVE_SCORE;
    }
    if (move.capture) {
        var target_piece = Chess.PE.P; // this could be p as well

        const pieces = switch (search.state.side) {
            .white => &[_]Chess.PE{ .p, .n, .b, .r, .q, .k },
            .black => &[_]Chess.PE{ .P, .N, .B, .R, .Q, .K },
        };

        for (pieces) |piece| {
            if (search.state.bitboards[@intFromEnum(piece)].isSet(move.target)) {
                target_piece = piece;
                break;
            }
        }
        return MVV_LVA[@intFromEnum(move.piece)][@intFromEnum(target_piece)] + CAPTURE_BOOST;
    } else {
        // score 1st killer move
        if (@as(BitMoveType, @bitCast(killer_moves[0][search.ply])) == @as(BitMoveType, @bitCast(move))) {
            return KILLER_FIRST;
        }

        // score 2nd killer move
        else if (@as(BitMoveType, @bitCast(killer_moves[1][search.ply])) == @as(BitMoveType, @bitCast(move))) {
            return KILLER_SECOND;
        }

        // score history moves
        else {
            return @as(
                isize,
                @intCast(history_moves[@intFromEnum(move.piece)][@intFromEnum(move.target)]),
            );
        }
        return 0;
    }

    return 0;
}

// most valuable victim & less valuable attacker
//    (Victims) Pawn Knight Bishop   Rook  Queen   King
//  (Attackers)
//        Pawn   105    205    305    405    505    605
//      Knight   104    204    304    404    504    604
//      Bishop   103    203    303    403    503    603
//        Rook   102    202    302    402    502    602
//       Queen   101    201    301    401    501    601
//        King   100    200    300    400    500    600

// MVV LVA [attacker][victim]
const MVV_LVA: [12][12]isize = .{
    .{ 105, 205, 305, 405, 505, 605, 105, 205, 305, 405, 505, 605 },
    .{ 104, 204, 304, 404, 504, 604, 104, 204, 304, 404, 504, 604 },
    .{ 103, 203, 303, 403, 503, 603, 103, 203, 303, 403, 503, 603 },
    .{ 102, 202, 302, 402, 502, 602, 102, 202, 302, 402, 502, 602 },
    .{ 101, 201, 301, 401, 501, 601, 101, 201, 301, 401, 501, 601 },
    .{ 100, 200, 300, 400, 500, 600, 100, 200, 300, 400, 500, 600 },

    .{ 105, 205, 305, 405, 505, 605, 105, 205, 305, 405, 505, 605 },
    .{ 104, 204, 304, 404, 504, 604, 104, 204, 304, 404, 504, 604 },
    .{ 103, 203, 303, 403, 503, 603, 103, 203, 303, 403, 503, 603 },
    .{ 102, 202, 302, 402, 502, 602, 102, 202, 302, 402, 502, 602 },
    .{ 101, 201, 301, 401, 501, 601, 101, 201, 301, 401, 501, 601 },
    .{ 100, 200, 300, 400, 500, 600, 100, 200, 300, 400, 500, 600 },
};