1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
use std::{error::Error, fmt::Display};

#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum PlayError {
    OutOfBounds,
    AlreadyOccupied,
    NoCapstone,
    NoStones,
    OpeningNonFlat,
    EmptySquare,
    StackNotOwned,
    StackError(StackError),
    TakeError(TakeError),
    SpreadOutOfBounds,
    GameOver,
}

impl Display for PlayError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        if let Self::StackError(stack_error) = self {
            stack_error.fmt(f)
        } else if let Self::TakeError(take_error) = self {
            take_error.fmt(f)
        } else {
            match self {
                Self::OutOfBounds => "given square is not on the board",
                Self::AlreadyOccupied => {
                    "cannot place a piece in that position because it is already occupied"
                }
                Self::NoCapstone => "there is not a capstone left to play",
                Self::NoStones => "there are no more stones left to play",
                Self::OpeningNonFlat => "cannot play a wall or capstone on the first two plies",
                Self::EmptySquare => "cannot move from an empty square",
                Self::StackNotOwned => "cannot move a stack that you do not own",
                Self::SpreadOutOfBounds => "spread would leave the board",
                Self::GameOver => "cannot play a move after the game is over",
                Self::StackError(_) | Self::TakeError(_) => unreachable!(),
            }
            .fmt(f)
        }
    }
}

impl Error for PlayError {}

impl From<TakeError> for PlayError {
    fn from(e: TakeError) -> Self {
        Self::TakeError(e)
    }
}

impl From<StackError> for PlayError {
    fn from(e: StackError) -> Self {
        Self::StackError(e)
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum StackError {
    Wall,
    Cap,
}

impl Display for StackError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Cap => "cannot stack on top of a capstone",
            Self::Wall => "can only flatten a wall with a capstone",
        }
        .fmt(f)
    }
}

impl Error for StackError {}

#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum TakeError {
    Zero,
    CarryLimit,
    StackSize,
}

impl Display for TakeError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Zero => "cannot take 0 from a stack",
            Self::CarryLimit => "cannot take more than the carry limit",
            Self::StackSize => "cannot take more pieces than there are on the stack",
        }
        .fmt(f)
    }
}

impl Error for TakeError {}