//
// jja: swiss army knife for chess file formats
// src/ctg.rs: CTG constants and utilities
//
// Copyright (c) 2023 Ali Polatel <alip@chesswob.org>
// Based in part upon remoteglot which is:
//     Copyright 2007 Steinar H. Gunderson <steinar+remoteglot@gunderson.no>
//     Licensed under the GNU General Public License, version 2.
//
// SPDX-License-Identifier: GPL-3.0-or-later

use std::{
    fmt::{self, Display, Formatter},
    io::{Error, ErrorKind},
};

use console::style;
use shakmaty::{uci::Uci, Board, Color, Role, Square};

/// A structure representing page boundaries.
#[derive(Debug)]
pub struct PageBounds {
    /// Padding
    pub pad: i32,
    /// Low page bound
    pub low: i32,
    /// High page bound
    pub high: i32,
}

/// A structure representing a chess board.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct ByteBoard {
    /// Chess board represented as a set of bytes.
    pub bytes: [u8; 64],
}

/// An enumeration representing Numeric Annotation Glyphs (NAGs)
///
/// # NAG Groupings
///
/// - `$0`: provided for the convenience of software designers as a placeholder value; should not
/// appear in PGN files and has no typographic representation.
/// - `$1-$9`: Move assessments (used by jja for weight/priority calculation during CTG->{BIN,ABK}
/// conversion)
/// - `$10-$135`: Positional assessments
/// - `$136-$139`: Time Pressure Commentaries
/// - `$140+`: not defined (some non-standard NAGs defined in this range)
#[derive(Debug, Eq, PartialEq, Copy, Clone, PartialOrd, Ord)]
#[repr(u8)]
pub enum Nag {
    /// Null annotation
    Null,
    /// A good move, commonly known as "!"
    Good,
    /// A mistake, commonly known as "?"
    Mistake,
    /// A hard move, commonly known as "!!"
    Hard,
    /// A blunder, commonly known as "??"
    Blunder,
    /// An interesting move, commonly known as "!?"
    Interesting,
    /// A dubious move, commonly known as "?!"
    Dubious,
    /// A forced move, also known as "□"
    Forced,
    /// A singular move (no reasonable alternatives)
    Only,
    /// Worst move
    Worst,
    /// Drawish position or even
    Drawish,
    /// Equal chances, quiet position
    EqualQuiet,
    /// Equal chances, active position
    EqualActive,
    /// Unclear position
    Unclear,
    /// White has a slight advantage
    SlightEdgeWhite,
    /// Black has a slight advantage
    SlightEdgeBlack,
    /// White has a moderate advantage
    ModerateAdvantageWhite,
    /// Black has a moderate advantage
    ModerateAdvantageBlack,
    /// White has a decisive advantage
    DecisiveAdvantageWhite,
    /// Black has a decisive advantage
    DecisiveAdvantageBlack,
    /// White has a crushing advantage (Black should resign)
    CrushingAdvantageWhite,
    /// Black has a crushing advantage (White should resign)
    CrushingAdvantageBlack,
    /// White is in zugzwang, also known as "⊙"
    ZugzwangWhite,
    /// Black is in zugzwang, also known as "⊙"
    ZugzwangBlack,
    /// White has a slight space advantage
    SlightSpaceAdvantageWhite,
    /// Black has a slight space advantage
    SlightSpaceAdvantageBlack,
    /// White has a moderate space advantage, also known as "○"
    ModerateSpaceAdvantageWhite,
    /// Black has a moderate space advantage, also known as "○"
    ModerateSpaceAdvantageBlack,
    /// White has a decisive space advantage
    DecisiveSpaceAdvantageWhite,
    /// Black has a decisive space advantage
    DecisiveSpaceAdvantageBlack,
    /// White has a slight time (development) advantage
    SlightDevelopmentAdvantageWhite,
    /// Black has a slight time (development) advantage
    SlightDevelopmentAdvantageBlack,
    /// White has a moderate time (development) advantage, also known as "⟳"
    ModerateDevelopmentAdvantageWhite,
    /// Black has a moderate time (development) advantage, also known as "⟳"
    ModerateDevelopmentAdvantageBlack,
    /// White has a decisive time (development) advantage
    DecisiveDevelopmentAdvantageWhite,
    /// Black has a decisive time (development) advantage
    DecisiveDevelopmentAdvantageBlack,
    /// White has the initiative, also known as "↑"
    InitiativeWhite,
    /// Black has the initiative, also known as "↑"
    InitiativeBlack,
    /// White has a lasting initiative
    LastingInitiativeWhite,
    /// Black has a lasting initiative
    LastingInitiativeBlack,
    /// White has the attack, also known as "→"
    AttackWhite,
    /// Black has the attack, also known as "→"
    AttackBlack,
    /// White has insufficient compensation for material deficit
    InsufficientCompensationWhite,
    /// Black has insufficient compensation for material deficit
    InsufficientCompensationBlack,
    /// White has sufficient compensation for material deficit, also known as "⯹"
    SufficientCompensationWhite,
    /// Black has sufficient compensation for material deficit, also known as "⯹"
    SufficientCompensationBlack,
    /// White has more than adequate compensation for material deficit
    MoreThanAdequateCompensationWhite,
    /// Black has more than adequate compensation for material deficit
    MoreThanAdequateCompensationBlack,
    /// White has a slight center control advantage
    SlightCenterControlAdvantageWhite,
    /// Black has a slight center control advantage
    SlightCenterControlAdvantageBlack,
    /// White has a moderate center control advantage
    ModerateCenterControlAdvantageWhite,
    /// Black has a moderate center control advantage
    ModerateCenterControlAdvantageBlack,
    /// White has a decisive center control advantage
    DecisiveCenterControlAdvantageWhite,
    /// Black has a decisive center control advantage
    DecisiveCenterControlAdvantageBlack,
    /// White has a slight kingside control advantage
    SlightKingSideControlAdvantageWhite,
    /// Black has a slight kingside control advantage
    SlightKingSideControlAdvantageBlack,
    /// White has a moderate kingside control advantage
    ModerateKingSideControlAdvantageWhite,
    /// Black has a moderate kingside control advantage
    ModerateKingSideControlAdvantageBlack,
    /// White has a decisive kingside control advantage
    DecisiveKingSideControlAdvantageWhite,
    /// Black has a decisive kingside control advantage
    DecisiveKingSideControlAdvantageBlack,
    /// White has a slight queenside control advantage
    SlightQueenSideControlAdvantageWhite,
    /// Black has a slight queenside control advantage
    SlightQueenSideControlAdvantageBlack,
    /// White has a moderate queenside control advantage
    ModerateQueenSideControlAdvantageWhite,
    /// Black has a moderate queenside control advantage
    ModerateQueenSideControlAdvantageBlack,
    /// White has a decisive queenside control advantage
    DecisiveQueenSideControlAdvantageWhite,
    /// Black has a decisive queenside control advantage
    DecisiveQueenSideControlAdvantageBlack,
    /// White has a vulnerable first rank
    VulnerableFirstRankWhite,
    /// Black has a vulnerable first rank
    VulnerableFirstRankBlack,
    /// White has a well protected first rank
    WellProtectedFirstRankWhite,
    /// Black has a well protected first rank
    WellProtectedFirstRankBlack,
    /// White has a poorly protected king
    PoorlyProtectedKingWhite,
    /// Black has a poorly protected king
    PoorlyProtectedKingBlack,
    /// White has a well protected king
    WellProtectedKingWhite,
    /// Black has a well protected king
    WellProtectedKingBlack,
    /// White has a poorly placed king
    PoorlyPlacedKingWhite,
    /// Black has a poorly placed king
    PoorlyPlacedKingBlack,
    /// White has a well placed king
    WellPlacedKingWhite,
    /// Black has a well placed king
    WellPlacedKingBlack,
    /// White has a very weak pawn structure
    VeryWeakPawnStructureWhite,
    /// Black has a very weak pawn structure
    VeryWeakPawnStructureBlack,
    /// White has a moderately weak pawn structure
    ModeratelyWeakPawnStructureWhite,
    /// Black has a moderately weak pawn structure
    ModeratelyWeakPawnStructureBlack,
    /// White has a moderately strong pawn structure
    ModeratelyStrongPawnStructureWhite,
    /// Black has a moderately strong pawn structure
    ModeratelyStrongPawnStructureBlack,
    /// White has a very strong pawn structure
    VeryStrongPawnStructureWhite,
    /// Black has a very strong pawn structure
    VeryStrongPawnStructureBlack,
    /// White has poor knight placement
    PoorKnightPlacementWhite,
    /// Black has poor knight placement
    PoorKnightPlacementBlack,
    /// White has good knight placement
    GoodKnightPlacementWhite,
    /// Black has good knight placement
    GoodKnightPlacementBlack,
    /// White has poor bishop placement
    PoorBishopPlacementWhite,
    /// Black has poor bishop placement
    PoorBishopPlacementBlack,
    /// White has good bishop placement
    GoodBishopPlacementWhite,
    /// Black has good bishop placement
    GoodBishopPlacementBlack,
    /// White has poor rook placement
    PoorRookPlacementWhite,
    /// Black has poor rook placement
    PoorRookPlacementBlack,
    /// White has good rook placement
    GoodRookPlacementWhite,
    /// Black has good rook placement
    GoodRookPlacementBlack,
    /// White has poor queen placement
    PoorQueenPlacementWhite,
    /// Black has poor queen placement
    PoorQueenPlacementBlack,
    /// White has good queen placement
    GoodQueenPlacementWhite,
    /// Black has good queen placement
    GoodQueenPlacementBlack,
    /// White has poor piece coordination
    PoorPieceCoordinationWhite,
    /// Black has poor piece coordination
    PoorPieceCoordinationBlack,
    /// White has good piece coordination
    GoodPieceCoordinationWhite,
    /// Black has good piece coordination
    GoodPieceCoordinationBlack,
    /// White has played the opening very poorly
    VeryPoorOpeningPlayWhite,
    /// Black has played the opening very poorly
    VeryPoorOpeningPlayBlack,
    /// White has played the opening poorly
    PoorOpeningPlayWhite,
    /// Black has played the opening poorly
    PoorOpeningPlayBlack,
    /// White has played the opening well
    WellOpeningPlayWhite,
    /// Black has played the opening well
    WellOpeningPlayBlack,
    /// White has played the opening very well
    VeryWellOpeningPlayWhite,
    /// Black has played the opening very well
    VeryWellOpeningPlayBlack,
    /// White has played the middlegame very poorly
    VeryPoorMiddleGamePlayWhite,
    /// Black has played the middlegame very poorly
    VeryPoorMiddleGamePlayBlack,
    /// White has played the middlegame poorly
    PoorMiddleGamePlayWhite,
    /// Black has played the middlegame poorly
    PoorMiddleGamePlayBlack,
    /// White has played the middlegame well
    WellMiddleGamePlayWhite,
    /// Black has played the middlegame well
    WellMiddleGamePlayBlack,
    /// White has played the middlegame very well
    VeryWellMiddleGamePlayWhite,
    /// Black has played the middlegame very well
    VeryWellMiddleGamePlayBlack,
    /// White has played the ending very poorly
    VeryPoorEndingPlayWhite,
    /// Black has played the ending very poorly
    VeryPoorEndingPlayBlack,
    /// White has played the ending poorly
    PoorEndingPlayWhite,
    /// Black has played the ending poorly
    PoorEndingPlayBlack,
    /// White has played the ending well
    WellEndingPlayWhite,
    /// Black has played the ending well
    WellEndingPlayBlack,
    /// White has played the ending very well
    VeryWellEndingPlayWhite,
    /// Black has played the ending very well
    VeryWellEndingPlayBlack,
    /// White has slight counterplay
    SlightCounterPlayWhite,
    /// Black has slight counterplay
    SlightCounterPlayBlack,
    /// White has moderate counterplay, also known as "⇆"
    ModerateCounterPlayWhite,
    /// Black has moderate counterplay, also known as "⇆"
    ModerateCounterPlayBlack,
    /// White has decisive counterplay, also known as "⇆"
    DecisiveCounterPlayWhite,
    /// Black has decisive counterplay, also known as "⇆"
    DecisiveCounterPlayBlack,
    /// White has moderate time control pressure
    ModerateTimeControlPressureWhite,
    /// Black has moderate time control pressure
    ModerateTimeControlPressureBlack,
    /// White has severe time control pressure, also known as zeitnot or "⨁"
    SevereTimeControlPressureWhite,
    /// Black has severe time control pressure, also known as zeitnot or "⨁"
    SevereTimeControlPressureBlack,
    /// With the idea..., also known as "∆"
    WithTheIdea,
    /// Aimed against..., also known as "∇"
    AimedAgainst,
    /// Better is..., also known as "⌓"
    BetterIs,
    /// Worse is..., also known as "<="
    WorseIs,
    /// Equivalent is..., also known as "=="
    EquivalentIs,
    /// Editorial Comment, also known as "RR"
    EditorialComment,
    /// Novelty, also known as "N"
    Novelty,
    /// Undefined
    Nag147,
    /// Undefined
    Nag148,
    /// Undefined
    Nag149,
    /// Undefined
    Nag150,
    /// Undefined
    Nag151,
    /// Undefined
    Nag152,
    /// Undefined
    Nag153,
    /// Undefined
    Nag154,
    /// Undefined
    Nag155,
    /// Undefined
    Nag156,
    /// Undefined
    Nag157,
    /// Undefined
    Nag158,
    /// Undefined
    Nag159,
    /// Undefined
    Nag160,
    /// Undefined
    Nag161,
    /// Undefined
    Nag162,
    /// Undefined
    Nag163,
    /// Undefined
    Nag164,
    /// Undefined
    Nag165,
    /// Undefined
    Nag166,
    /// Undefined
    Nag167,
    /// Undefined
    Nag168,
    /// Undefined
    Nag169,
    /// Undefined
    Nag170,
    /// Undefined
    Nag171,
    /// Undefined
    Nag172,
    /// Undefined
    Nag173,
    /// Undefined
    Nag174,
    /// Undefined
    Nag175,
    /// Undefined
    Nag176,
    /// Undefined
    Nag177,
    /// Undefined
    Nag178,
    /// Undefined
    Nag179,
    /// Undefined
    Nag180,
    /// Undefined
    Nag181,
    /// Undefined
    Nag182,
    /// Undefined
    Nag183,
    /// Undefined
    Nag184,
    /// Undefined
    Nag185,
    /// Undefined
    Nag186,
    /// Undefined
    Nag187,
    /// Undefined
    Nag188,
    /// Undefined
    Nag189,
    /// Undefined
    Nag190,
    /// Undefined
    Nag191,
    /// Undefined
    Nag192,
    /// Undefined
    Nag193,
    /// Undefined
    Nag194,
    /// Undefined
    Nag195,
    /// Undefined
    Nag196,
    /// Undefined
    Nag197,
    /// Undefined
    Nag198,
    /// Undefined
    Nag199,
    /// Undefined
    Nag200,
    /// Undefined
    Nag201,
    /// Undefined
    Nag202,
    /// Undefined
    Nag203,
    /// Undefined
    Nag204,
    /// Undefined
    Nag205,
    /// Undefined
    Nag206,
    /// Undefined
    Nag207,
    /// Undefined
    Nag208,
    /// Undefined
    Nag209,
    /// Undefined
    Nag210,
    /// Undefined
    Nag211,
    /// Undefined
    Nag212,
    /// Undefined
    Nag213,
    /// Undefined
    Nag214,
    /// Undefined
    Nag215,
    /// Undefined
    Nag216,
    /// Undefined
    Nag217,
    /// Undefined
    Nag218,
    /// Undefined
    Nag219,
    /// Diagram, also known as "⬒"
    Diagram,
    /// Diagram (from Black), also known as "⬓"
    DiagramFromBlack,
    /// Undefined
    Nag222,
    /// Undefined
    Nag223,
    /// Undefined
    Nag224,
    /// Undefined
    Nag225,
    /// Undefined
    Nag226,
    /// Undefined
    Nag227,
    /// Undefined
    Nag228,
    /// Undefined
    Nag229,
    /// Undefined
    Nag230,
    /// Undefined
    Nag231,
    /// Undefined
    Nag232,
    /// Undefined
    Nag233,
    /// Undefined
    Nag234,
    /// Undefined
    Nag235,
    /// Undefined
    Nag236,
    /// Undefined
    Nag237,
    /// Space advantage, also known as "○"
    SpaceAdvantage,
    /// File (columns on the chessboard labeled a-h), also known as "⇔"
    File,
    /// Diagonal, also known as "⇗"
    Diagonal,
    /// Centre, also known as "⊞"
    Centre,
    /// King-side, also known as "⟫"
    KingSide,
    /// Queen-side, also known as "⟪"
    QueenSide,
    /// Weak point, also known as "✕"
    WeakPoint,
    /// Ending, also known as "⊥"
    Ending,
    /// Bishop pair
    BishopPair,
    /// Opposite bishops
    OppositeBishops,
    /// Same bishops
    SameBishops,
    /// Connected pawns, also known as "⯺"
    ConnectedPawns,
    /// Isolated pawns, also known as "⯻"
    IsolatedPawns,
    /// Doubled pawns, also known as "⯼"
    DoubledPawns,
    /// Passed pawn, also known as "⯽"
    PassedPawn,
    /// Pawn majority
    PawnMajority,
    /// With, also known as "∟"
    With,
    /// Without, also known as "⯾"
    Without,
}

impl Display for Nag {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match self {
            Nag::Null => write!(f, " "),
            Nag::Hard => write!(f, "!!"),
            Nag::Good => write!(f, "!"),
            Nag::Mistake => write!(f, "?"),
            Nag::Blunder => write!(f, "??"),
            Nag::Interesting => write!(f, "!?"),
            Nag::Dubious => write!(f, "?!"),
            Nag::Forced | Nag::Only => write!(f, "□"),
            Nag::ZugzwangWhite | Nag::ZugzwangBlack => write!(f, "⊙"),
            Nag::ModerateSpaceAdvantageWhite | Nag::ModerateSpaceAdvantageBlack => write!(f, "○"),
            Nag::ModerateDevelopmentAdvantageWhite | Nag::ModerateDevelopmentAdvantageBlack => {
                write!(f, "⟳")
            }
            Nag::InitiativeWhite | Nag::InitiativeBlack => write!(f, "↑"),
            Nag::AttackWhite | Nag::AttackBlack => write!(f, "→"),
            Nag::SufficientCompensationWhite | Nag::SufficientCompensationBlack => write!(f, "⯹"),
            Nag::ModerateCounterPlayWhite
            | Nag::DecisiveCounterPlayWhite
            | Nag::ModerateCounterPlayBlack
            | Nag::DecisiveCounterPlayBlack => write!(f, "⇆"),
            Nag::SevereTimeControlPressureWhite | Nag::SevereTimeControlPressureBlack => {
                write!(f, "⨁")
            }
            Nag::WithTheIdea => write!(f, "∆"),
            Nag::AimedAgainst => write!(f, "∇"),
            Nag::BetterIs => write!(f, "⌓"),
            Nag::WorseIs => write!(f, "<="),
            Nag::EquivalentIs => write!(f, "=="),
            Nag::EditorialComment => write!(f, "RR"),
            Nag::Novelty => write!(f, "N"),
            Nag::Diagram => write!(f, "⬒"),
            Nag::DiagramFromBlack => write!(f, "⬓"),
            Nag::SpaceAdvantage => write!(f, "○"),
            Nag::File => write!(f, "⇔"),
            Nag::Diagonal => write!(f, "⇗"),
            Nag::Centre => write!(f, "⊞"),
            Nag::KingSide => write!(f, "⟫"),
            Nag::QueenSide => write!(f, "⟪"),
            Nag::WeakPoint => write!(f, "✕"),
            Nag::Ending => write!(f, "⊥"),
            Nag::ConnectedPawns => write!(f, "⯺"),
            Nag::IsolatedPawns => write!(f, "⯻"),
            Nag::DoubledPawns => write!(f, "⯼"),
            Nag::PassedPawn => write!(f, "⯽"),
            Nag::With => write!(f, "∟"),
            Nag::Without => write!(f, "⯾"),
            n => write!(f, "${}", n),
        }
    }
}

impl From<u8> for Nag {
    fn from(num: u8) -> Self {
        match num {
            0 => Nag::Null,
            1 => Nag::Good,
            2 => Nag::Mistake,
            3 => Nag::Hard,
            4 => Nag::Blunder,
            5 => Nag::Interesting,
            6 => Nag::Dubious,
            7 => Nag::Forced,
            8 => Nag::Only,
            9 => Nag::Worst,
            10 => Nag::Drawish,
            11 => Nag::EqualQuiet,
            12 => Nag::EqualActive,
            13 => Nag::Unclear,
            14 => Nag::SlightEdgeWhite,
            15 => Nag::SlightEdgeBlack,
            16 => Nag::ModerateAdvantageWhite,
            17 => Nag::ModerateAdvantageBlack,
            18 => Nag::DecisiveAdvantageWhite,
            19 => Nag::DecisiveAdvantageBlack,
            20 => Nag::CrushingAdvantageWhite,
            21 => Nag::CrushingAdvantageBlack,
            22 => Nag::ZugzwangWhite,
            23 => Nag::ZugzwangBlack,
            24 => Nag::SlightSpaceAdvantageWhite,
            25 => Nag::SlightSpaceAdvantageBlack,
            26 => Nag::ModerateSpaceAdvantageWhite,
            27 => Nag::ModerateSpaceAdvantageBlack,
            28 => Nag::DecisiveSpaceAdvantageWhite,
            29 => Nag::DecisiveSpaceAdvantageBlack,
            30 => Nag::SlightDevelopmentAdvantageWhite,
            31 => Nag::SlightDevelopmentAdvantageBlack,
            32 => Nag::ModerateDevelopmentAdvantageWhite,
            33 => Nag::ModerateDevelopmentAdvantageBlack,
            34 => Nag::DecisiveDevelopmentAdvantageWhite,
            35 => Nag::DecisiveDevelopmentAdvantageBlack,
            36 => Nag::InitiativeWhite,
            37 => Nag::InitiativeBlack,
            38 => Nag::LastingInitiativeWhite,
            39 => Nag::LastingInitiativeBlack,
            40 => Nag::AttackWhite,
            41 => Nag::AttackBlack,
            42 => Nag::InsufficientCompensationWhite,
            43 => Nag::InsufficientCompensationBlack,
            44 => Nag::SufficientCompensationWhite,
            45 => Nag::SufficientCompensationBlack,
            46 => Nag::MoreThanAdequateCompensationWhite,
            47 => Nag::MoreThanAdequateCompensationBlack,
            48 => Nag::SlightCenterControlAdvantageWhite,
            49 => Nag::SlightCenterControlAdvantageBlack,
            50 => Nag::ModerateCenterControlAdvantageWhite,
            51 => Nag::ModerateCenterControlAdvantageBlack,
            52 => Nag::DecisiveCenterControlAdvantageWhite,
            53 => Nag::DecisiveCenterControlAdvantageBlack,
            54 => Nag::SlightKingSideControlAdvantageWhite,
            55 => Nag::SlightKingSideControlAdvantageBlack,
            56 => Nag::ModerateKingSideControlAdvantageWhite,
            57 => Nag::ModerateKingSideControlAdvantageBlack,
            58 => Nag::DecisiveKingSideControlAdvantageWhite,
            59 => Nag::DecisiveKingSideControlAdvantageBlack,
            60 => Nag::SlightQueenSideControlAdvantageWhite,
            61 => Nag::SlightQueenSideControlAdvantageBlack,
            62 => Nag::ModerateQueenSideControlAdvantageWhite,
            63 => Nag::ModerateQueenSideControlAdvantageBlack,
            64 => Nag::DecisiveQueenSideControlAdvantageWhite,
            65 => Nag::DecisiveQueenSideControlAdvantageBlack,
            66 => Nag::VulnerableFirstRankWhite,
            67 => Nag::VulnerableFirstRankBlack,
            68 => Nag::WellProtectedFirstRankWhite,
            69 => Nag::WellProtectedFirstRankBlack,
            70 => Nag::PoorlyProtectedKingWhite,
            71 => Nag::PoorlyProtectedKingBlack,
            72 => Nag::WellProtectedKingWhite,
            73 => Nag::WellProtectedKingBlack,
            74 => Nag::PoorlyPlacedKingWhite,
            75 => Nag::PoorlyPlacedKingBlack,
            76 => Nag::WellPlacedKingWhite,
            77 => Nag::WellPlacedKingBlack,
            78 => Nag::VeryWeakPawnStructureWhite,
            79 => Nag::VeryWeakPawnStructureBlack,
            80 => Nag::ModeratelyWeakPawnStructureWhite,
            81 => Nag::ModeratelyWeakPawnStructureBlack,
            82 => Nag::ModeratelyStrongPawnStructureWhite,
            83 => Nag::ModeratelyStrongPawnStructureBlack,
            84 => Nag::VeryStrongPawnStructureWhite,
            85 => Nag::VeryStrongPawnStructureBlack,
            86 => Nag::PoorKnightPlacementWhite,
            87 => Nag::PoorKnightPlacementBlack,
            88 => Nag::GoodKnightPlacementWhite,
            89 => Nag::GoodKnightPlacementBlack,
            90 => Nag::PoorBishopPlacementWhite,
            91 => Nag::PoorBishopPlacementBlack,
            92 => Nag::GoodBishopPlacementWhite,
            93 => Nag::GoodBishopPlacementBlack,
            94 => Nag::PoorRookPlacementWhite,
            95 => Nag::PoorRookPlacementBlack,
            96 => Nag::GoodRookPlacementWhite,
            97 => Nag::GoodRookPlacementBlack,
            98 => Nag::PoorQueenPlacementWhite,
            99 => Nag::PoorQueenPlacementBlack,
            100 => Nag::GoodQueenPlacementWhite,
            101 => Nag::GoodQueenPlacementBlack,
            102 => Nag::PoorPieceCoordinationWhite,
            103 => Nag::PoorPieceCoordinationBlack,
            104 => Nag::GoodPieceCoordinationWhite,
            105 => Nag::GoodPieceCoordinationBlack,
            106 => Nag::VeryPoorOpeningPlayWhite,
            107 => Nag::VeryPoorOpeningPlayBlack,
            108 => Nag::PoorOpeningPlayWhite,
            109 => Nag::PoorOpeningPlayBlack,
            110 => Nag::WellOpeningPlayWhite,
            111 => Nag::WellOpeningPlayBlack,
            112 => Nag::VeryWellOpeningPlayWhite,
            113 => Nag::VeryWellOpeningPlayBlack,
            114 => Nag::VeryPoorMiddleGamePlayWhite,
            115 => Nag::VeryPoorMiddleGamePlayBlack,
            116 => Nag::PoorMiddleGamePlayWhite,
            117 => Nag::PoorMiddleGamePlayBlack,
            118 => Nag::WellMiddleGamePlayWhite,
            119 => Nag::WellMiddleGamePlayBlack,
            120 => Nag::VeryWellMiddleGamePlayWhite,
            121 => Nag::VeryWellMiddleGamePlayBlack,
            122 => Nag::VeryPoorEndingPlayWhite,
            123 => Nag::VeryPoorEndingPlayBlack,
            124 => Nag::PoorEndingPlayWhite,
            125 => Nag::PoorEndingPlayBlack,
            126 => Nag::WellEndingPlayWhite,
            127 => Nag::WellEndingPlayBlack,
            128 => Nag::VeryWellEndingPlayWhite,
            129 => Nag::VeryWellEndingPlayBlack,
            130 => Nag::SlightCounterPlayWhite,
            131 => Nag::SlightCounterPlayBlack,
            132 => Nag::ModerateCounterPlayWhite,
            133 => Nag::ModerateCounterPlayBlack,
            134 => Nag::DecisiveCounterPlayWhite,
            135 => Nag::DecisiveCounterPlayBlack,
            136 => Nag::ModerateTimeControlPressureWhite,
            137 => Nag::ModerateTimeControlPressureBlack,
            138 => Nag::SevereTimeControlPressureWhite,
            139 => Nag::SevereTimeControlPressureBlack,
            140 => Nag::WithTheIdea,
            141 => Nag::AimedAgainst,
            142 => Nag::BetterIs,
            143 => Nag::WorseIs,
            144 => Nag::EquivalentIs,
            145 => Nag::EditorialComment,
            146 => Nag::Novelty,
            147 => Nag::Nag147,
            148 => Nag::Nag148,
            149 => Nag::Nag149,
            150 => Nag::Nag150,
            151 => Nag::Nag151,
            152 => Nag::Nag152,
            153 => Nag::Nag153,
            154 => Nag::Nag154,
            155 => Nag::Nag155,
            156 => Nag::Nag156,
            157 => Nag::Nag157,
            158 => Nag::Nag158,
            159 => Nag::Nag159,
            160 => Nag::Nag160,
            161 => Nag::Nag161,
            162 => Nag::Nag162,
            163 => Nag::Nag163,
            164 => Nag::Nag164,
            165 => Nag::Nag165,
            166 => Nag::Nag166,
            167 => Nag::Nag167,
            168 => Nag::Nag168,
            169 => Nag::Nag169,
            170 => Nag::Nag170,
            171 => Nag::Nag171,
            172 => Nag::Nag172,
            173 => Nag::Nag173,
            174 => Nag::Nag174,
            175 => Nag::Nag175,
            176 => Nag::Nag176,
            177 => Nag::Nag177,
            178 => Nag::Nag178,
            179 => Nag::Nag179,
            180 => Nag::Nag180,
            181 => Nag::Nag181,
            182 => Nag::Nag182,
            183 => Nag::Nag183,
            184 => Nag::Nag184,
            185 => Nag::Nag185,
            186 => Nag::Nag186,
            187 => Nag::Nag187,
            188 => Nag::Nag188,
            189 => Nag::Nag189,
            190 => Nag::Nag190,
            191 => Nag::Nag191,
            192 => Nag::Nag192,
            193 => Nag::Nag193,
            194 => Nag::Nag194,
            195 => Nag::Nag195,
            196 => Nag::Nag196,
            197 => Nag::Nag197,
            198 => Nag::Nag198,
            199 => Nag::Nag199,
            200 => Nag::Nag200,
            201 => Nag::Nag201,
            202 => Nag::Nag202,
            203 => Nag::Nag203,
            204 => Nag::Nag204,
            205 => Nag::Nag205,
            206 => Nag::Nag206,
            207 => Nag::Nag207,
            208 => Nag::Nag208,
            209 => Nag::Nag209,
            210 => Nag::Nag210,
            211 => Nag::Nag211,
            212 => Nag::Nag212,
            213 => Nag::Nag213,
            214 => Nag::Nag214,
            215 => Nag::Nag215,
            216 => Nag::Nag216,
            217 => Nag::Nag217,
            218 => Nag::Nag218,
            219 => Nag::Nag219,
            220 => Nag::Diagram,
            221 => Nag::DiagramFromBlack,
            222 => Nag::Nag222,
            223 => Nag::Nag223,
            224 => Nag::Nag224,
            225 => Nag::Nag225,
            226 => Nag::Nag226,
            227 => Nag::Nag227,
            228 => Nag::Nag228,
            229 => Nag::Nag229,
            230 => Nag::Nag230,
            231 => Nag::Nag231,
            232 => Nag::Nag232,
            233 => Nag::Nag233,
            234 => Nag::Nag234,
            235 => Nag::Nag235,
            236 => Nag::Nag236,
            237 => Nag::Nag237,
            238 => Nag::SpaceAdvantage,
            239 => Nag::File,
            240 => Nag::Diagonal,
            241 => Nag::Centre,
            242 => Nag::KingSide,
            243 => Nag::QueenSide,
            244 => Nag::WeakPoint,
            245 => Nag::Ending,
            246 => Nag::BishopPair,
            247 => Nag::OppositeBishops,
            248 => Nag::SameBishops,
            249 => Nag::ConnectedPawns,
            250 => Nag::IsolatedPawns,
            251 => Nag::DoubledPawns,
            252 => Nag::PassedPawn,
            253 => Nag::PawnMajority,
            254 => Nag::With,
            255 => Nag::Without,
        }
    }
}

impl Nag {
    /// Returns `true` if the Nag is `Interesting`, `Dubious`, `Mistake`, or `Blunder`, `false`
    /// otherwise.
    pub fn is_question_mark(&self) -> bool {
        matches!(
            self,
            Nag::Interesting | Nag::Dubious | Nag::Mistake | Nag::Blunder | Nag::Worst
        )
    }
}

impl ByteBoard {
    /// Creates a new empty chessboard.
    ///
    /// This method creates a new `ByteBoard` object representing an empty
    /// chessboard, with all squares set to the space character (`' '`).
    ///
    /// # Returns
    ///
    /// * `ByteBoard` - A new `ByteBoard` object representing an empty chessboard.
    pub fn new(board_str: &str) -> Self {
        let bytes: [u8; 64] = board_str.as_bytes().try_into().unwrap();
        Self { bytes }
    }

    /// Creates a new `ByteBoard` from the given byte slice.
    ///
    /// This method creates a new `ByteBoard` object using the given byte
    /// slice. The slice should have exactly 64 elements representing
    /// the pieces on the chessboard.
    ///
    /// # Arguments
    ///
    /// * `slice: &[u8; 64]` - A byte slice containing the board
    /// representation.
    ///
    /// # Returns
    ///
    /// * `ByteBoard` - A new `ByteBoard`
    pub fn from_bytes(slice: &[u8; 64]) -> Self {
        Self { bytes: *slice }
    }

    /// Creates a new `ByteBoard` from a `shakmaty` `Board` object.
    ///
    /// This method creates a new `ByteBoard` object from a given `shakmaty` `Board` object,
    /// where each byte in the internal array represents a piece or an empty square (with the space
    /// character, `' '`, for empty squares).
    ///
    /// # Arguments
    ///
    /// * `board` - A `shakmaty` `Board` object representing the chess board.
    ///
    /// # Returns
    ///
    /// * `ByteBoard` - A new `ByteBoard` object representing the chess board.
    pub fn from_board(board: &Board) -> Self {
        let mut bytes: [u8; 64] = [b' '; 64];

        let mut set_piece_byte = |role, color, byte: u8| {
            for square in board.by_role(role) & board.by_color(color) {
                let (file, rank) = square.coords();
                let index = (7 - rank as usize) * 8 + file as usize;
                bytes[index] = byte;
            }
        };

        // Order optimized for cache efficiency.
        set_piece_byte(Role::Pawn, Color::Black, b'p');
        set_piece_byte(Role::Pawn, Color::White, b'P');
        set_piece_byte(Role::Knight, Color::Black, b'n');
        set_piece_byte(Role::Knight, Color::White, b'N');
        set_piece_byte(Role::Bishop, Color::Black, b'b');
        set_piece_byte(Role::Bishop, Color::White, b'B');
        set_piece_byte(Role::Rook, Color::Black, b'r');
        set_piece_byte(Role::Rook, Color::White, b'R');
        set_piece_byte(Role::Queen, Color::Black, b'q');
        set_piece_byte(Role::Queen, Color::White, b'Q');
        set_piece_byte(Role::King, Color::Black, b'k');
        set_piece_byte(Role::King, Color::White, b'K');

        Self { bytes }
    }

    /// Gets a byte slice representation of the chessboard.
    ///
    /// This method returns a reference to the internal byte array
    /// representing the chessboard. Each byte in the array corresponds to a
    /// piece or an empty square, where the space character (`' '`) represents
    /// an empty square.
    ///
    /// # Returns
    ///
    /// * `&[u8]` - A byte slice representing the chessboard.
    pub fn as_bytes(&self) -> &[u8] {
        &self.bytes
    }

    /// Decodes a FEN (Forsyth-Edwards Notation) string into a `ByteBoard` object.
    ///
    /// This method converts a FEN string representing a chessboard position
    /// into a `ByteBoard` object, where each byte in the internal array represents
    /// a piece or an empty square (with the space character, `' '`, for empty squares).
    ///
    /// # Arguments
    ///
    /// * `fen` - A FEN string representing the chessboard position.
    ///
    /// # Returns
    ///
    /// * `Result<ByteBoard, Error>` - A `ByteBoard` object representing the chessboard if
    ///   the input FEN string is valid, or an `Error` if the input FEN string is invalid.
    pub fn from_fen(fen: &[u8]) -> Result<Self, Error> {
        // Create a new board array, initially filled with spaces
        let mut bytes = [b' '; 64];
        // Keep track of the current index on the board
        let mut i = 0;

        // Iterate over each character in the input FEN string
        for &ch in fen {
            match ch {
                // If we encounter a valid piece character, place it at the current index on the board
                b'r' | b'n' | b'b' | b'q' | b'k' | b'p' | b'R' | b'N' | b'B' | b'Q' | b'K'
                | b'P' => {
                    // Ensure we're not out of bounds on the array (which should never happen if the input is a valid FEN string)
                    if i < 64 {
                        bytes[i] = ch;
                        i += 1;
                    } else {
                        return Err(Error::from(ErrorKind::InvalidInput));
                    }
                }
                // If we encounter a digit, skip the appropriate number of spaces on the board
                b'1'..=b'8' => {
                    let spaces = ch - b'0'; // Convert the character to a number
                    i = (i + spaces as usize).min(64); // Increment the index by that number, but ensure we don't go out of bounds
                }
                // If we encounter a '/', we're moving to the next row
                b'/' => {
                    // Adjust index to next line start considering the empty spots at the end of the rank.
                    // We calculate the remainder when current index is divided by 8 (which is the board width)
                    // If remainder is not zero, it means that we haven't finished current row yet (we have empty spots at the end)
                    // So, we add (8 - remainder) to the index to move it to the start of the next row
                    let remainder = i % 8;
                    if remainder != 0 {
                        i += 8 - remainder;
                    }
                }
                // A space indicates the end of the board section of the FEN string
                b' ' => {
                    break;
                }
                // If we encounter an unexpected character, it's an error
                _ => {
                    return Err(Error::from(ErrorKind::InvalidInput));
                }
            }
        }

        // If we made it through the entire loop without returning an error, our board is good to go!
        Ok(Self { bytes })
    }

    /// Searches for a specific chess piece on the board and returns its position.
    ///
    /// This method searches for the given `piece` character in the `ByteBoard` object
    /// and returns the position of the `num`-th occurrence of the piece.
    /// Board positions are represented as i32 values (0 to 63).
    ///
    /// # Arguments
    ///
    /// * `piece` - The character representing the chess piece to search for.
    /// * `num` - The occurrence number of the piece to search for.
    ///
    /// # Returns
    ///
    /// * `Option<i32>` - The position of the `num`-th occurrence of `piece` on the board, or an
    /// `None` if the piece is not found.
    pub fn find_piece(&self, piece: char, num: i32) -> Option<i32> {
        let piece_byte = piece as u8;
        let mut occurrence = num;

        for x in 0..8 {
            // SAFETY: Loop bounds guarantee safe conversion in range.
            let ux: usize = unsafe { x.try_into().unwrap_unchecked() };

            for y in 0..8 {
                // SAFETY: Loop bounds guarantee safe conversion in range.
                let uy: usize = unsafe { y.try_into().unwrap_unchecked() };

                let index = (7 - uy) * 8 + ux;
                if self.bytes[index] != piece_byte {
                    continue;
                }
                if occurrence == 1 {
                    return Some(y * 8 + x);
                }
                occurrence -= 1;
            }
        }

        None
    }

    /// Inverts the colors of the pieces on the chessboard and flips the board vertically.
    ///
    /// This method inverts the colors of the pieces on the `ByteBoard` object by changing
    /// the case of each character, and flips the board vertically. The resulting board
    /// is stored in a new `ByteBoard` object.
    ///
    /// # Returns
    ///
    /// * `ByteBoard` - A new `ByteBoard` object representing the inverted and flipped chessboard.
    pub fn invert(&mut self) {
        for y in 0..4 {
            for x in 0..8 {
                let index1 = y * 8 + x;
                let index2 = (7 - y) * 8 + x;

                self.bytes.swap(index1, index2);
                self.bytes[index1] = invert_color(self.bytes[index1]);
                self.bytes[index2] = invert_color(self.bytes[index2]);
            }
        }
    }

    /// Determines if a chessboard should be flipped.
    ///
    /// This method checks if the given chessboard should be flipped. A board is flipped when
    /// neither side can castle, and the white king is on the lower half of the board. This
    /// function must not be called if either side can castle.
    ///
    /// # Returns
    ///
    /// * `bool` - `true` if the board should be flipped, `false` otherwise.
    pub fn needs_flipping(&self) -> bool {
        self.bytes[0..32].contains(&b'K')
    }

    /// Flips a chessboard horizontally and returns the flipped en passant square.
    ///
    /// This method takes a mutable reference to a `ByteBoard` object and an en passant square option,
    /// flips the board horizontally, and returns the flipped en passant square as an option.
    ///
    /// # Arguments
    ///
    /// * `eps` - An optional `shakmaty::Square` representing the en passant square.
    ///
    /// # Returns
    ///
    /// * `Option<Square>` - An optional `shakmaty::Square` containing the flipped en passant
    /// square.
    pub fn flip(&mut self, eps: Option<Square>) -> Option<Square> {
        // Flip the board horizontally
        self.bytes.reverse();

        eps.map(|square| square.flip_horizontal())
    }
}

impl std::fmt::Display for ByteBoard {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", String::from_utf8_lossy(&self.bytes))
    }
}

/// Inverts the color of a chess piece character by switching its case.
///
/// This function takes a character representing a chess piece and returns the
/// corresponding character for the piece with the opposite color. If the input
/// character is not a valid chess piece character, it returns the input character
/// unchanged.
///
/// # Arguments
///
/// * `c` - A character representing a chess piece ("PNBRQK" or lowercase).
///
/// # Returns
///
/// * `char` - The character representing the chess piece with the opposite color.
#[inline(always)]
fn invert_color(c: u8) -> u8 {
    match c {
        b'p' => b'P',
        b'P' => b'p',
        b'n' => b'N',
        b'N' => b'n',
        b'b' => b'B',
        b'B' => b'b',
        b'r' => b'R',
        b'R' => b'r',
        b'q' => b'Q',
        b'Q' => b'q',
        b'k' => b'K',
        b'K' => b'k',
        _ => c,
    }
}

// A constant table of 64-bit values used in the `gen_hash` function.
const TBL2: [u32; 64] = [
    0x3100d2bf, 0x3118e3de, 0x34ab1372, 0x2807a847, 0x1633f566, 0x2143b359, 0x26d56488, 0x3b9e6f59,
    0x37755656, 0x3089ca7b, 0x18e92d85, 0x0cd0e9d8, 0x1a9e3b54, 0x3eaa902f, 0x0d9bfaae, 0x2f32b45b,
    0x31ed6102, 0x3d3c8398, 0x146660e3, 0x0f8d4b76, 0x02c77a5f, 0x146c8799, 0x1c47f51f, 0x249f8f36,
    0x24772043, 0x1fbc1e4d, 0x1e86b3fa, 0x37df36a6, 0x16ed30e4, 0x02c3148e, 0x216e5929, 0x0636b34e,
    0x317f9f56, 0x15f09d70, 0x131026fb, 0x38c784b1, 0x29ac3305, 0x2b485dc5, 0x3c049ddc, 0x35a9fbcd,
    0x31d5373b, 0x2b246799, 0x0a2923d3, 0x08a96e9d, 0x30031a9f, 0x08f525b5, 0x33611c06, 0x2409db98,
    0x0ca4feb2, 0x1000b71e, 0x30566e32, 0x39447d31, 0x194e3752, 0x08233a95, 0x0f38fe36, 0x29c7cd57,
    0x0f7b3a39, 0x328e8a16, 0x1e7d1388, 0x0fba78f5, 0x274c7e7c, 0x1e8be65c, 0x2fa0b0bb, 0x1eb6c371,
];

/// Returns a colored string representation of a UCI move according to the recommendation and NAGs.
///
/// This function takes a recommendation value, NAGs, and a UCI move as input.
/// If the recommendation ix 0xa4, the move will be colored green.
/// If the recommendation is 0x80, the move will be colored green, unless the NAGs
/// include the '?' character, in which case the move will be colored blue.
/// If the recommendation is 0x40, the move will be colored red.
/// For any other recommendation, the UCI move will not be colored.
///
/// # Arguments
///
/// * `uci` - A `shakmaty::uci::Uci` representing the UCI move.
/// * `nag` - An `Option<Nag>` containing the NAG for the move.
/// * `recommendation` - An unsigned 8-bit integer representing the recommendation value.
pub fn colored_uci(uci: &Uci, nag: &Option<Nag>, recommendation: u8) -> String {
    let is_question_mark = nag.map(|n| n.is_question_mark()).unwrap_or(false);

    match recommendation {
        0xa4 => style(uci.to_string()).green().to_string(),
        0x80 => {
            if is_question_mark {
                style(uci.to_string()).blue().to_string()
            } else {
                style(uci.to_string()).green().to_string()
            }
        }
        0x40 => style(uci.to_string()).red().to_string(),
        _ => uci.to_string(),
    }
}

/// Generates a hash value for the input slice of i8 integers.
///
/// This function takes a slice of i8 integers and generates a hash value
/// using a custom algorithm that incorporates the values in the `TBL2` constant.
///
/// # Arguments
///
/// * `ptr` - A slice of i8 integers to generate the hash value for.
/// * `len` - The length of the input slice.
///
/// # Returns
///
/// * `i32` - The generated hash value.
#[inline(always)]
pub fn gen_hash(ptr: &[i8], len: usize) -> i32 {
    let mut hash: i32 = 0;
    let mut tmp: i16 = 0;

    for ch in ptr.iter().take(len) {
        let ch = i16::from(*ch);
        tmp += ((0x0f - (ch & 0x0f)) << 2) + 1;
        hash = hash.wrapping_add(TBL2[(tmp & 0x3f) as usize] as i32);
        tmp += ((0xf0 - (ch & 0xf0)) >> 2) + 1;
        hash = hash.wrapping_add(TBL2[(tmp & 0x3f) as usize] as i32);
    }

    hash
}

/// A struct representing an encoded chess move.
///
/// This struct is used to store information about a chess move, including the
/// move encoding, the piece involved, the number of moves, and the directions
/// of the move (forward and right).
pub struct MoveEnc {
    /// An `u8` representing the move encoding.
    pub encoding: u8,
    /// A `char` representing the chess piece involved in the move.
    pub piece: char,
    /// An `i32` representing the number of moves.
    pub num: i32,
    /// An `i32` representing the forward direction of the move.
    pub forward: i32,
    /// An `i32` representing the right direction of the move.
    pub right: i32,
}

/// A constant representing the maximum number of elements in the `MOVETABLE`.
pub const MOVETABLE_MAX: usize = 166;

/// A static array of `MoveEnc` structs representing a table of encoded chess moves.
///
/// This table is used to store information about encoded chess moves for use
/// in move generation and validation.
pub static MOVETABLE: [MoveEnc; MOVETABLE_MAX] = [
    MoveEnc {
        encoding: 0x00,
        piece: 'P',
        num: 5,
        forward: 1,
        right: 1,
    },
    MoveEnc {
        encoding: 0x01,
        piece: 'N',
        num: 2,
        forward: -1,
        right: -2,
    },
    MoveEnc {
        encoding: 0x03,
        piece: 'Q',
        num: 2,
        forward: 0,
        right: 2,
    },
    MoveEnc {
        encoding: 0x04,
        piece: 'P',
        num: 2,
        forward: 1,
        right: 0,
    },
    MoveEnc {
        encoding: 0x05,
        piece: 'Q',
        num: 1,
        forward: 1,
        right: 0,
    },
    MoveEnc {
        encoding: 0x06,
        piece: 'P',
        num: 4,
        forward: 1,
        right: -1,
    },
    MoveEnc {
        encoding: 0x08,
        piece: 'Q',
        num: 2,
        forward: 0,
        right: 4,
    },
    MoveEnc {
        encoding: 0x09,
        piece: 'B',
        num: 2,
        forward: 6,
        right: 6,
    },
    MoveEnc {
        encoding: 0x0a,
        piece: 'K',
        num: 1,
        forward: -1,
        right: 0,
    },
    MoveEnc {
        encoding: 0x0c,
        piece: 'P',
        num: 1,
        forward: 1,
        right: -1,
    },
    MoveEnc {
        encoding: 0x0d,
        piece: 'B',
        num: 1,
        forward: 3,
        right: 3,
    },
    MoveEnc {
        encoding: 0x0e,
        piece: 'R',
        num: 2,
        forward: 0,
        right: 3,
    },
    MoveEnc {
        encoding: 0x0f,
        piece: 'N',
        num: 1,
        forward: -1,
        right: -2,
    },
    MoveEnc {
        encoding: 0x12,
        piece: 'B',
        num: 1,
        forward: 7,
        right: 7,
    },
    MoveEnc {
        encoding: 0x13,
        piece: 'K',
        num: 1,
        forward: 1,
        right: 0,
    },
    MoveEnc {
        encoding: 0x14,
        piece: 'P',
        num: 8,
        forward: 1,
        right: 1,
    },
    MoveEnc {
        encoding: 0x15,
        piece: 'B',
        num: 1,
        forward: 5,
        right: 5,
    },
    MoveEnc {
        encoding: 0x18,
        piece: 'P',
        num: 7,
        forward: 1,
        right: 0,
    },
    MoveEnc {
        encoding: 0x1a,
        piece: 'Q',
        num: 2,
        forward: 6,
        right: 0,
    },
    MoveEnc {
        encoding: 0x1b,
        piece: 'B',
        num: 1,
        forward: 1,
        right: -1,
    },
    MoveEnc {
        encoding: 0x1d,
        piece: 'B',
        num: 2,
        forward: 7,
        right: 7,
    },
    MoveEnc {
        encoding: 0x21,
        piece: 'R',
        num: 2,
        forward: 0,
        right: 7,
    },
    MoveEnc {
        encoding: 0x22,
        piece: 'B',
        num: 2,
        forward: 2,
        right: -2,
    },
    MoveEnc {
        encoding: 0x23,
        piece: 'Q',
        num: 2,
        forward: 6,
        right: 6,
    },
    MoveEnc {
        encoding: 0x24,
        piece: 'P',
        num: 8,
        forward: 1,
        right: -1,
    },
    MoveEnc {
        encoding: 0x26,
        piece: 'B',
        num: 1,
        forward: 7,
        right: -7,
    },
    MoveEnc {
        encoding: 0x27,
        piece: 'P',
        num: 3,
        forward: 1,
        right: -1,
    },
    MoveEnc {
        encoding: 0x28,
        piece: 'Q',
        num: 1,
        forward: 5,
        right: 5,
    },
    MoveEnc {
        encoding: 0x29,
        piece: 'Q',
        num: 1,
        forward: 0,
        right: 6,
    },
    MoveEnc {
        encoding: 0x2a,
        piece: 'N',
        num: 2,
        forward: -2,
        right: 1,
    },
    MoveEnc {
        encoding: 0x2d,
        piece: 'P',
        num: 6,
        forward: 1,
        right: 1,
    },
    MoveEnc {
        encoding: 0x2e,
        piece: 'B',
        num: 1,
        forward: 1,
        right: 1,
    },
    MoveEnc {
        encoding: 0x2f,
        piece: 'Q',
        num: 1,
        forward: 0,
        right: 1,
    },
    MoveEnc {
        encoding: 0x30,
        piece: 'N',
        num: 2,
        forward: -2,
        right: -1,
    },
    MoveEnc {
        encoding: 0x31,
        piece: 'Q',
        num: 1,
        forward: 0,
        right: 3,
    },
    MoveEnc {
        encoding: 0x32,
        piece: 'B',
        num: 2,
        forward: 5,
        right: 5,
    },
    MoveEnc {
        encoding: 0x34,
        piece: 'N',
        num: 1,
        forward: 2,
        right: 1,
    },
    MoveEnc {
        encoding: 0x36,
        piece: 'N',
        num: 1,
        forward: 1,
        right: 2,
    },
    MoveEnc {
        encoding: 0x37,
        piece: 'Q',
        num: 1,
        forward: 4,
        right: 0,
    },
    MoveEnc {
        encoding: 0x38,
        piece: 'Q',
        num: 2,
        forward: 4,
        right: -4,
    },
    MoveEnc {
        encoding: 0x39,
        piece: 'Q',
        num: 1,
        forward: 0,
        right: 5,
    },
    MoveEnc {
        encoding: 0x3a,
        piece: 'B',
        num: 1,
        forward: 6,
        right: 6,
    },
    MoveEnc {
        encoding: 0x3b,
        piece: 'Q',
        num: 2,
        forward: 5,
        right: -5,
    },
    MoveEnc {
        encoding: 0x3c,
        piece: 'B',
        num: 1,
        forward: 5,
        right: -5,
    },
    MoveEnc {
        encoding: 0x41,
        piece: 'Q',
        num: 2,
        forward: 5,
        right: 5,
    },
    MoveEnc {
        encoding: 0x42,
        piece: 'Q',
        num: 1,
        forward: 7,
        right: -7,
    },
    MoveEnc {
        encoding: 0x44,
        piece: 'K',
        num: 1,
        forward: -1,
        right: 1,
    },
    MoveEnc {
        encoding: 0x45,
        piece: 'Q',
        num: 1,
        forward: 3,
        right: 3,
    },
    MoveEnc {
        encoding: 0x4a,
        piece: 'P',
        num: 8,
        forward: 2,
        right: 0,
    },
    MoveEnc {
        encoding: 0x4b,
        piece: 'Q',
        num: 1,
        forward: 5,
        right: -5,
    },
    MoveEnc {
        encoding: 0x4c,
        piece: 'N',
        num: 2,
        forward: 2,
        right: 1,
    },
    MoveEnc {
        encoding: 0x4d,
        piece: 'Q',
        num: 2,
        forward: 1,
        right: 0,
    },
    MoveEnc {
        encoding: 0x50,
        piece: 'R',
        num: 1,
        forward: 6,
        right: 0,
    },
    MoveEnc {
        encoding: 0x52,
        piece: 'R',
        num: 1,
        forward: 0,
        right: 6,
    },
    MoveEnc {
        encoding: 0x54,
        piece: 'B',
        num: 2,
        forward: 1,
        right: -1,
    },
    MoveEnc {
        encoding: 0x55,
        piece: 'P',
        num: 3,
        forward: 1,
        right: 0,
    },
    MoveEnc {
        encoding: 0x5c,
        piece: 'P',
        num: 7,
        forward: 1,
        right: 1,
    },
    MoveEnc {
        encoding: 0x5f,
        piece: 'P',
        num: 5,
        forward: 2,
        right: 0,
    },
    MoveEnc {
        encoding: 0x61,
        piece: 'Q',
        num: 1,
        forward: 6,
        right: 6,
    },
    MoveEnc {
        encoding: 0x62,
        piece: 'P',
        num: 2,
        forward: 2,
        right: 0,
    },
    MoveEnc {
        encoding: 0x63,
        piece: 'Q',
        num: 2,
        forward: 7,
        right: -7,
    },
    MoveEnc {
        encoding: 0x66,
        piece: 'B',
        num: 1,
        forward: 3,
        right: -3,
    },
    MoveEnc {
        encoding: 0x67,
        piece: 'K',
        num: 1,
        forward: 1,
        right: 1,
    },
    MoveEnc {
        encoding: 0x69,
        piece: 'R',
        num: 2,
        forward: 7,
        right: 0,
    },
    MoveEnc {
        encoding: 0x6a,
        piece: 'B',
        num: 1,
        forward: 4,
        right: 4,
    },
    MoveEnc {
        /* short castling */
        encoding: 0x6b,
        piece: 'K',
        num: 1,
        forward: 0,
        right: 2,
    },
    MoveEnc {
        encoding: 0x6e,
        piece: 'R',
        num: 1,
        forward: 0,
        right: 5,
    },
    MoveEnc {
        encoding: 0x6f,
        piece: 'Q',
        num: 2,
        forward: 7,
        right: 7,
    },
    MoveEnc {
        encoding: 0x72,
        piece: 'B',
        num: 2,
        forward: 7,
        right: -7,
    },
    MoveEnc {
        encoding: 0x74,
        piece: 'Q',
        num: 1,
        forward: 0,
        right: 2,
    },
    MoveEnc {
        encoding: 0x79,
        piece: 'B',
        num: 2,
        forward: 6,
        right: -6,
    },
    MoveEnc {
        encoding: 0x7a,
        piece: 'R',
        num: 1,
        forward: 3,
        right: 0,
    },
    MoveEnc {
        encoding: 0x7b,
        piece: 'R',
        num: 2,
        forward: 6,
        right: 0,
    },
    MoveEnc {
        encoding: 0x7c,
        piece: 'P',
        num: 3,
        forward: 1,
        right: 1,
    },
    MoveEnc {
        encoding: 0x7d,
        piece: 'R',
        num: 2,
        forward: 1,
        right: 0,
    },
    MoveEnc {
        encoding: 0x7e,
        piece: 'Q',
        num: 1,
        forward: 3,
        right: -3,
    },
    MoveEnc {
        encoding: 0x7f,
        piece: 'R',
        num: 1,
        forward: 0,
        right: 1,
    },
    MoveEnc {
        encoding: 0x80,
        piece: 'Q',
        num: 1,
        forward: 6,
        right: -6,
    },
    MoveEnc {
        encoding: 0x81,
        piece: 'R',
        num: 1,
        forward: 1,
        right: 0,
    },
    MoveEnc {
        encoding: 0x82,
        piece: 'P',
        num: 6,
        forward: 1,
        right: -1,
    },
    MoveEnc {
        encoding: 0x85,
        piece: 'N',
        num: 1,
        forward: 2,
        right: -1,
    },
    MoveEnc {
        encoding: 0x86,
        piece: 'R',
        num: 1,
        forward: 0,
        right: 7,
    },
    MoveEnc {
        encoding: 0x87,
        piece: 'R',
        num: 1,
        forward: 5,
        right: 0,
    },
    MoveEnc {
        encoding: 0x8a,
        piece: 'N',
        num: 1,
        forward: -2,
        right: 1,
    },
    MoveEnc {
        encoding: 0x8b,
        piece: 'P',
        num: 1,
        forward: 1,
        right: 1,
    },
    MoveEnc {
        encoding: 0x8c,
        piece: 'K',
        num: 1,
        forward: -1,
        right: -1,
    },
    MoveEnc {
        encoding: 0x8e,
        piece: 'Q',
        num: 2,
        forward: 2,
        right: -2,
    },
    MoveEnc {
        encoding: 0x8f,
        piece: 'Q',
        num: 1,
        forward: 0,
        right: 7,
    },
    MoveEnc {
        encoding: 0x92,
        piece: 'Q',
        num: 2,
        forward: 1,
        right: 1,
    },
    MoveEnc {
        encoding: 0x94,
        piece: 'Q',
        num: 1,
        forward: 3,
        right: 0,
    },
    MoveEnc {
        encoding: 0x96,
        piece: 'P',
        num: 2,
        forward: 1,
        right: 1,
    },
    MoveEnc {
        encoding: 0x97,
        piece: 'K',
        num: 1,
        forward: 0,
        right: -1,
    },
    MoveEnc {
        encoding: 0x98,
        piece: 'R',
        num: 1,
        forward: 0,
        right: 3,
    },
    MoveEnc {
        encoding: 0x99,
        piece: 'R',
        num: 1,
        forward: 4,
        right: 0,
    },
    MoveEnc {
        encoding: 0x9a,
        piece: 'Q',
        num: 1,
        forward: 6,
        right: 0,
    },
    MoveEnc {
        encoding: 0x9b,
        piece: 'P',
        num: 3,
        forward: 2,
        right: 0,
    },
    MoveEnc {
        encoding: 0x9d,
        piece: 'Q',
        num: 1,
        forward: 2,
        right: 0,
    },
    MoveEnc {
        encoding: 0x9f,
        piece: 'B',
        num: 2,
        forward: 4,
        right: -4,
    },
    MoveEnc {
        encoding: 0xa0,
        piece: 'Q',
        num: 2,
        forward: 3,
        right: 0,
    },
    MoveEnc {
        encoding: 0xa2,
        piece: 'Q',
        num: 1,
        forward: 2,
        right: 2,
    },
    MoveEnc {
        encoding: 0xa3,
        piece: 'P',
        num: 8,
        forward: 1,
        right: 0,
    },
    MoveEnc {
        encoding: 0xa5,
        piece: 'R',
        num: 2,
        forward: 5,
        right: 0,
    },
    MoveEnc {
        encoding: 0xa9,
        piece: 'R',
        num: 2,
        forward: 0,
        right: 2,
    },
    MoveEnc {
        encoding: 0xab,
        piece: 'Q',
        num: 2,
        forward: 6,
        right: -6,
    },
    MoveEnc {
        encoding: 0xad,
        piece: 'R',
        num: 2,
        forward: 0,
        right: 4,
    },
    MoveEnc {
        encoding: 0xae,
        piece: 'Q',
        num: 2,
        forward: 3,
        right: 3,
    },
    MoveEnc {
        encoding: 0xb0,
        piece: 'Q',
        num: 2,
        forward: 4,
        right: 0,
    },
    MoveEnc {
        encoding: 0xb1,
        piece: 'P',
        num: 6,
        forward: 2,
        right: 0,
    },
    MoveEnc {
        encoding: 0xb2,
        piece: 'B',
        num: 1,
        forward: 6,
        right: -6,
    },
    MoveEnc {
        encoding: 0xb5,
        piece: 'R',
        num: 2,
        forward: 0,
        right: 5,
    },
    MoveEnc {
        encoding: 0xb7,
        piece: 'Q',
        num: 1,
        forward: 5,
        right: 0,
    },
    MoveEnc {
        encoding: 0xb9,
        piece: 'B',
        num: 2,
        forward: 3,
        right: 3,
    },
    MoveEnc {
        encoding: 0xbb,
        piece: 'P',
        num: 5,
        forward: 1,
        right: 0,
    },
    MoveEnc {
        encoding: 0xbc,
        piece: 'Q',
        num: 2,
        forward: 0,
        right: 5,
    },
    MoveEnc {
        encoding: 0xbd,
        piece: 'Q',
        num: 2,
        forward: 2,
        right: 0,
    },
    MoveEnc {
        encoding: 0xbe,
        piece: 'K',
        num: 1,
        forward: 0,
        right: 1,
    },
    MoveEnc {
        encoding: 0xc1,
        piece: 'B',
        num: 1,
        forward: 2,
        right: 2,
    },
    MoveEnc {
        encoding: 0xc2,
        piece: 'B',
        num: 2,
        forward: 2,
        right: 2,
    },
    MoveEnc {
        encoding: 0xc3,
        piece: 'B',
        num: 1,
        forward: 2,
        right: -2,
    },
    MoveEnc {
        encoding: 0xc4,
        piece: 'R',
        num: 2,
        forward: 0,
        right: 1,
    },
    MoveEnc {
        encoding: 0xc5,
        piece: 'R',
        num: 2,
        forward: 4,
        right: 0,
    },
    MoveEnc {
        encoding: 0xc6,
        piece: 'Q',
        num: 2,
        forward: 5,
        right: 0,
    },
    MoveEnc {
        encoding: 0xc7,
        piece: 'P',
        num: 7,
        forward: 1,
        right: -1,
    },
    MoveEnc {
        encoding: 0xc8,
        piece: 'P',
        num: 7,
        forward: 2,
        right: 0,
    },
    MoveEnc {
        encoding: 0xc9,
        piece: 'Q',
        num: 2,
        forward: 7,
        right: 0,
    },
    MoveEnc {
        encoding: 0xca,
        piece: 'B',
        num: 2,
        forward: 3,
        right: -3,
    },
    MoveEnc {
        encoding: 0xcb,
        piece: 'P',
        num: 6,
        forward: 1,
        right: 0,
    },
    MoveEnc {
        encoding: 0xcc,
        piece: 'B',
        num: 2,
        forward: 5,
        right: -5,
    },
    MoveEnc {
        encoding: 0xcd,
        piece: 'R',
        num: 1,
        forward: 0,
        right: 2,
    },
    MoveEnc {
        encoding: 0xcf,
        piece: 'P',
        num: 4,
        forward: 1,
        right: 0,
    },
    MoveEnc {
        encoding: 0xd1,
        piece: 'P',
        num: 2,
        forward: 1,
        right: -1,
    },
    MoveEnc {
        encoding: 0xd2,
        piece: 'N',
        num: 2,
        forward: 1,
        right: 2,
    },
    MoveEnc {
        encoding: 0xd3,
        piece: 'N',
        num: 2,
        forward: 1,
        right: -2,
    },
    MoveEnc {
        encoding: 0xd7,
        piece: 'Q',
        num: 1,
        forward: 1,
        right: -1,
    },
    MoveEnc {
        encoding: 0xd8,
        piece: 'R',
        num: 2,
        forward: 0,
        right: 6,
    },
    MoveEnc {
        encoding: 0xd9,
        piece: 'Q',
        num: 1,
        forward: 2,
        right: -2,
    },
    MoveEnc {
        encoding: 0xda,
        piece: 'N',
        num: 1,
        forward: -2,
        right: -1,
    },
    MoveEnc {
        encoding: 0xdb,
        piece: 'P',
        num: 1,
        forward: 2,
        right: 0,
    },
    MoveEnc {
        encoding: 0xde,
        piece: 'P',
        num: 5,
        forward: 1,
        right: -1,
    },
    MoveEnc {
        encoding: 0xdf,
        piece: 'K',
        num: 1,
        forward: 1,
        right: -1,
    },
    MoveEnc {
        encoding: 0xe0,
        piece: 'N',
        num: 2,
        forward: -1,
        right: 2,
    },
    MoveEnc {
        encoding: 0xe1,
        piece: 'R',
        num: 1,
        forward: 7,
        right: 0,
    },
    MoveEnc {
        encoding: 0xe3,
        piece: 'R',
        num: 2,
        forward: 3,
        right: 0,
    },
    MoveEnc {
        encoding: 0xe5,
        piece: 'Q',
        num: 1,
        forward: 0,
        right: 4,
    },
    MoveEnc {
        encoding: 0xe6,
        piece: 'P',
        num: 4,
        forward: 2,
        right: 0,
    },
    MoveEnc {
        encoding: 0xe7,
        piece: 'Q',
        num: 1,
        forward: 4,
        right: 4,
    },
    MoveEnc {
        encoding: 0xe8,
        piece: 'R',
        num: 1,
        forward: 2,
        right: 0,
    },
    MoveEnc {
        encoding: 0xe9,
        piece: 'N',
        num: 1,
        forward: -1,
        right: 2,
    },
    MoveEnc {
        encoding: 0xeb,
        piece: 'P',
        num: 4,
        forward: 1,
        right: 1,
    },
    MoveEnc {
        encoding: 0xec,
        piece: 'P',
        num: 1,
        forward: 1,
        right: 0,
    },
    MoveEnc {
        encoding: 0xed,
        piece: 'Q',
        num: 1,
        forward: 7,
        right: 7,
    },
    MoveEnc {
        encoding: 0xee,
        piece: 'Q',
        num: 2,
        forward: 1,
        right: -1,
    },
    MoveEnc {
        encoding: 0xef,
        piece: 'R',
        num: 1,
        forward: 0,
        right: 4,
    },
    MoveEnc {
        encoding: 0xf0,
        piece: 'Q',
        num: 2,
        forward: 0,
        right: 7,
    },
    MoveEnc {
        encoding: 0xf1,
        piece: 'Q',
        num: 1,
        forward: 1,
        right: 1,
    },
    MoveEnc {
        encoding: 0xf3,
        piece: 'N',
        num: 2,
        forward: 2,
        right: -1,
    },
    MoveEnc {
        encoding: 0xf4,
        piece: 'R',
        num: 2,
        forward: 2,
        right: 0,
    },
    MoveEnc {
        encoding: 0xf5,
        piece: 'B',
        num: 2,
        forward: 1,
        right: 1,
    },
    MoveEnc {
        /* long castling */
        encoding: 0xf6,
        piece: 'K',
        num: 1,
        forward: 0,
        right: -2,
    },
    MoveEnc {
        encoding: 0xf7,
        piece: 'N',
        num: 1,
        forward: 1,
        right: -2,
    },
    MoveEnc {
        encoding: 0xf8,
        piece: 'Q',
        num: 2,
        forward: 0,
        right: 1,
    },
    MoveEnc {
        encoding: 0xf9,
        piece: 'Q',
        num: 2,
        forward: 6,
        right: 0,
    },
    MoveEnc {
        encoding: 0xfa,
        piece: 'Q',
        num: 2,
        forward: 0,
        right: 3,
    },
    MoveEnc {
        encoding: 0xfb,
        piece: 'Q',
        num: 2,
        forward: 2,
        right: 2,
    },
    MoveEnc {
        encoding: 0xfd,
        piece: 'Q',
        num: 1,
        forward: 7,
        right: 0,
    },
    MoveEnc {
        encoding: 0xfe,
        piece: 'Q',
        num: 2,
        forward: 3,
        right: -3,
    },
];

#[cfg(test)]
mod tests {
    use shakmaty::*;

    use super::*;

    #[test]
    fn test_byteboard_from_board() {
        let pos = Chess::default();
        let epd = format!(
            "{}",
            shakmaty::fen::Epd::from_position(pos.clone(), EnPassantMode::PseudoLegal)
        );
        let bb0 = ByteBoard::from_fen(epd.as_bytes()).expect("invalid fen");
        let bb1 = ByteBoard::from_board(&pos.board());

        assert_eq!(bb0, bb1);
    }
}
