1
// This file is part of hnefatafl-copenhagen.
2
//
3
// hnefatafl-copenhagen is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU Affero General Public License as published by
5
// the Free Software Foundation, either version 3 of the License, or
6
// (at your option) any later version.
7
//
8
// hnefatafl-copenhagen is distributed in the hope that it will be useful,
9
// but WITHOUT ANY WARRANTY; without even the implied warranty of
10
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
// GNU Affero General Public License for more details.
12
//
13
// You should have received a copy of the GNU Affero General Public License
14
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
15
//
16
// SPDX-License-Identifier: AGPL-3.0-or-later
17
// SPDX-FileCopyrightText: 2025 David Campbell <david@hnefatafl.org>
18

            
19
use std::{collections::HashMap, fmt, str::FromStr};
20

            
21
use rustc_hash::{FxBuildHasher, FxHashSet};
22
use serde::{Deserialize, Serialize};
23
use thiserror::Error;
24

            
25
use crate::{
26
    characters::Characters,
27
    game::PreviousBoards,
28
    play::{BOARD_LETTERS, EXIT_SQUARES_11X11, EXIT_SQUARES_13X13, Plae, Play, Vertex},
29
    role::Role,
30
    space::Space,
31
    status::Status,
32
};
33

            
34
pub const STARTING_POSITION_11X11: [&str; 11] = [
35
    "...XXXXX...",
36
    ".....X.....",
37
    "...........",
38
    "X....O....X",
39
    "X...OOO...X",
40
    "XX.OOKOO.XX",
41
    "X...OOO...X",
42
    "X....O....X",
43
    "...........",
44
    ".....X.....",
45
    "...XXXXX...",
46
];
47

            
48
pub const STARTING_POSITION_13X13: [&str; 13] = [
49
    "...XXXXXXX...",
50
    "......X......",
51
    ".............",
52
    "X.....O.....X",
53
    "X.....O.....X",
54
    "X....OOO....X",
55
    "XX.OOOKOOO.XX",
56
    "X....OOO....X",
57
    "X.....O.....X",
58
    "X.....O.....X",
59
    ".............",
60
    "......X......",
61
    "...XXXXXXX...",
62
];
63

            
64
#[derive(Clone, Deserialize, Eq, Hash, PartialEq, Serialize)]
65
pub struct Board {
66
    pub spaces: Vec<Space>,
67
    #[serde(skip)]
68
    pub display_ascii: bool,
69
}
70

            
71
impl Default for Board {
72
24705
    fn default() -> Self {
73
24705
        board_11x11()
74
24705
    }
75
}
76

            
77
impl fmt::Debug for Board {
78
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79
        let board_size: usize = self.size().into();
80

            
81
        writeln!(f)?;
82
        for y in 0..board_size {
83
            write!(f, r#"""#)?;
84

            
85
            for x in 0..board_size {
86
                match self.spaces[(y * board_size) + x] {
87
                    Space::Attacker => write!(f, "X")?,
88
                    Space::Empty => write!(f, ".")?,
89
                    Space::King => write!(f, "K")?,
90
                    Space::Defender => write!(f, "O")?,
91
                }
92
            }
93
            writeln!(f, r#"""#)?;
94
        }
95

            
96
        Ok(())
97
    }
98
}
99

            
100
impl fmt::Display for Board {
101
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102
        let board_size: usize = self.size().into();
103
        let mut letters = " ".repeat(3).clone();
104
        letters.push_str(&BOARD_LETTERS[..board_size]);
105
        let bar = "─".repeat(board_size);
106

            
107
        writeln!(f, "\n{letters}\n  ┌{bar}┐")?;
108
        for y in 0..board_size {
109
            let y_label = board_size - y;
110
            write!(f, "{y_label:2}│",)?;
111

            
112
            for x in 0..board_size {
113
                if (((y, x) == (0, 0)
114
                    || (y, x) == (10, 0)
115
                    || (y, x) == (0, 10)
116
                    || (y, x) == (10, 10)
117
                    || (y, x) == (5, 5))
118
                    && self.spaces[y * board_size + x] == Space::Empty
119
                    && board_size == 11)
120
                    || (((y, x) == (0, 0)
121
                        || (y, x) == (12, 0)
122
                        || (y, x) == (0, 12)
123
                        || (y, x) == (12, 12)
124
                        || (y, x) == (6, 6))
125
                        && self.spaces[y * board_size + x] == Space::Empty
126
                        && board_size == 13)
127
                {
128
                    if self.display_ascii {
129
                        write!(f, "#")?;
130
                    } else {
131
                        write!(f, "⌘")?;
132
                    }
133
                } else if self.display_ascii {
134
                    write!(f, "{}", self.spaces[y * board_size + x].display_ascii())?;
135
                } else {
136
                    write!(f, "{}", self.spaces[y * board_size + x])?;
137
                }
138
            }
139
            writeln!(f, "│{y_label:2}")?;
140
        }
141
        write!(f, "  └{bar}┘\n{letters}")
142
    }
143
}
144

            
145
impl TryFrom<[&str; 11]> for Board {
146
    type Error = anyhow::Error;
147

            
148
279
    fn try_from(value: [&str; 11]) -> anyhow::Result<Self> {
149
279
        let mut spaces = Vec::with_capacity(11 * 11);
150
279
        let mut kings = 0;
151

            
152
2964
        for (y, row) in value.iter().enumerate() {
153
32502
            for (x, ch) in row.chars().enumerate() {
154
32502
                let space = ch.try_into()?;
155
32502
                match space {
156
                    Space::Attacker | Space::Defender => {
157
1323
                        let vertex = Vertex {
158
1323
                            size: BoardSize::_11,
159
1323
                            x,
160
1323
                            y,
161
1323
                        };
162
1323
                        if vertex.on_restricted_square() {
163
15
                            return Err(anyhow::Error::msg(
164
15
                                "Only the king is allowed on restricted squares!",
165
15
                            ));
166
1308
                        }
167
                    }
168
30987
                    Space::Empty => {}
169
                    Space::King => {
170
192
                        kings += 1;
171
192
                        if kings > 1 {
172
3
                            return Err(anyhow::Error::msg("You can only have one king!"));
173
189
                        }
174
                    }
175
                }
176

            
177
32484
                spaces.push(space);
178
            }
179
        }
180

            
181
261
        Ok(Self {
182
261
            spaces,
183
261
            display_ascii: false,
184
261
        })
185
279
    }
186
}
187

            
188
impl TryFrom<[&str; 13]> for Board {
189
    type Error = anyhow::Error;
190

            
191
9
    fn try_from(value: [&str; 13]) -> anyhow::Result<Self> {
192
9
        let mut spaces = Vec::with_capacity(13 * 13);
193
9
        let mut kings = 0;
194

            
195
117
        for (y, row) in value.iter().enumerate() {
196
1521
            for (x, ch) in row.chars().enumerate() {
197
1521
                let space = ch.try_into()?;
198
1521
                match space {
199
                    Space::Attacker | Space::Defender => {
200
63
                        let vertex = Vertex {
201
63
                            size: BoardSize::_13,
202
63
                            x,
203
63
                            y,
204
63
                        };
205
63
                        if vertex.on_restricted_square() {
206
                            return Err(anyhow::Error::msg(
207
                                "Only the king is allowed on restricted squares!",
208
                            ));
209
63
                        }
210
                    }
211
1455
                    Space::Empty => {}
212
                    Space::King => {
213
3
                        kings += 1;
214
3
                        if kings > 1 {
215
                            return Err(anyhow::Error::msg("You can only have one king!"));
216
3
                        }
217
                    }
218
                }
219

            
220
1521
                spaces.push(space);
221
            }
222
        }
223

            
224
9
        Ok(Self {
225
9
            spaces,
226
9
            display_ascii: false,
227
9
        })
228
9
    }
229
}
230

            
231
impl Board {
232
    #[must_use]
233
24711
    pub fn new(board_size: BoardSize) -> Self {
234
24711
        match board_size {
235
24711
            BoardSize::_11 => board_11x11(),
236
            BoardSize::_13 => board_13x13(),
237
        }
238
24711
    }
239

            
240
141798
    fn able_to_move(&self, play_from: &Vertex) -> bool {
241
141798
        if let Some(vertex) = play_from.up()
242
109046
            && self.get(&vertex) == Space::Empty
243
        {
244
57415
            return true;
245
84383
        }
246

            
247
84383
        if let Some(vertex) = play_from.left()
248
62005
            && self.get(&vertex) == Space::Empty
249
        {
250
31440
            return true;
251
52943
        }
252

            
253
52943
        if let Some(vertex) = play_from.down()
254
49594
            && self.get(&vertex) == Space::Empty
255
        {
256
25634
            return true;
257
27309
        }
258

            
259
27309
        if let Some(vertex) = play_from.right()
260
21611
            && self.get(&vertex) == Space::Empty
261
        {
262
10699
            return true;
263
16610
        }
264

            
265
16610
        false
266
141798
    }
267

            
268
    #[must_use]
269
1214492
    pub fn a_legal_move_exists(
270
1214492
        &self,
271
1214492
        status: &Status,
272
1214492
        turn: &Role,
273
1214492
        previous_boards: &PreviousBoards,
274
1214492
    ) -> bool {
275
1214492
        let size = self.size();
276
1214492
        let board_size_usize: usize = size.into();
277

            
278
2417043
        for y in 0..board_size_usize {
279
19266560
            for x in 0..board_size_usize {
280
19266560
                let vertex_from = Vertex { size, x, y };
281
19266560
                if Role::from(self.get(&vertex_from)) != *turn {
282
18037258
                    continue;
283
1229302
                }
284

            
285
2043364
                for y in 0..board_size_usize {
286
13676106
                    for x in 0..board_size_usize {
287
13676106
                        let vertex_to = Vertex { size, x, y };
288

            
289
13676106
                        if vertex_to.x != vertex_from.x && vertex_to.y != vertex_from.y {
290
9789193
                            continue;
291
3886913
                        }
292

            
293
3886913
                        let play = Play {
294
3886913
                            role: *turn,
295
3886913
                            from: vertex_from,
296
3886913
                            to: vertex_to,
297
3886913
                        };
298

            
299
3886913
                        if let Ok(_board) = self.legal_move(&play, status, turn, previous_boards) {
300
1214188
                            return true;
301
2672725
                        }
302
                    }
303
                }
304
            }
305
        }
306

            
307
304
        false
308
1214492
    }
309

            
310
    #[must_use]
311
    pub fn captured(&self) -> Captured {
312
        let mut attacker = 0;
313
        let mut defender = 0;
314
        let mut king = true;
315

            
316
        for space in &self.spaces {
317
            match space {
318
                Space::Attacker => attacker += 1,
319
                Space::Empty => {}
320
                Space::King => king = false,
321
                Space::Defender => defender += 1,
322
            }
323
        }
324

            
325
        match self.size() {
326
            BoardSize::_11 => {
327
                attacker = 24 - attacker;
328
                defender = 12 - defender;
329
            }
330
            BoardSize::_13 => {
331
                attacker = 32 - attacker;
332
                defender = 16 - defender;
333
            }
334
        }
335

            
336
        Captured {
337
            attacker,
338
            defender,
339
            king,
340
        }
341
    }
342

            
343
1219372
    fn captures(&mut self, play_to: &Vertex, role_from: Role, captures: &mut Vec<Vertex>) {
344
1219372
        self.captures_(play_to, role_from, captures, super::play::Vertex::up);
345
1219372
        self.captures_(play_to, role_from, captures, super::play::Vertex::left);
346
1219372
        self.captures_(play_to, role_from, captures, super::play::Vertex::down);
347
1219372
        self.captures_(play_to, role_from, captures, super::play::Vertex::right);
348
1219372
    }
349

            
350
4877488
    fn captures_<T: Fn(&Vertex) -> Option<Vertex>>(
351
4877488
        &mut self,
352
4877488
        play_to: &Vertex,
353
4877488
        role_from: Role,
354
4877488
        captures: &mut Vec<Vertex>,
355
4877488
        over: T,
356
4877488
    ) {
357
4877488
        if let Some(right_1) = over(play_to) {
358
4623039
            let space = self.get(&right_1);
359
4623039
            if space != Space::King
360
4542062
                && Role::from(space) == role_from.opposite()
361
706395
                && let Some(right_2) = over(&right_1)
362
593306
                && ((right_2.on_restricted_square() && self.get(&right_2) != Space::King)
363
576090
                    || Role::from(self.get(&right_2)) == role_from)
364
182577
                && self.set_if_not_king(&right_1, Space::Empty)
365
182577
            {
366
182577
                captures.push(right_1);
367
4440462
            }
368
254449
        }
369
4877488
    }
370

            
371
    // y counts up going down.
372
    #[allow(clippy::too_many_lines)]
373
1219372
    fn captures_shield_wall(
374
1219372
        &mut self,
375
1219372
        role_from: Role,
376
1219372
        vertex_to: &Vertex,
377
1219372
        captures: &mut Vec<Vertex>,
378
1219372
    ) {
379
1219372
        let size = self.size();
380
1219372
        let board_size_usize: usize = size.into();
381

            
382
        // bottom row
383
13413104
        for x_1 in 0..board_size_usize {
384
13413104
            let vertex_1 = Vertex {
385
13413104
                size,
386
13413104
                x: x_1,
387
13413104
                y: board_size_usize - 1,
388
13413104
            };
389
13413104
            if Role::from(self.get(&vertex_1)) == role_from || vertex_1.on_restricted_square() {
390
4520015
                let mut count = 0;
391

            
392
4520015
                if x_1 == board_size_usize - 1 {
393
1219372
                    break;
394
3300643
                }
395
3300643
                let start = x_1 + 1;
396

            
397
3313316
                for x_2 in start..board_size_usize {
398
3313316
                    let vertex_2 = Vertex {
399
3313316
                        size,
400
3313316
                        x: x_2,
401
3313316
                        y: board_size_usize - 1,
402
3313316
                    };
403
3313316
                    let vertex_3 = Vertex {
404
3313316
                        size,
405
3313316
                        x: x_2,
406
3313316
                        y: board_size_usize - 2,
407
3313316
                    };
408
3313316
                    let role_2 = Role::from(self.get(&vertex_2));
409
3313316
                    let role_3 = Role::from(self.get(&vertex_3));
410
3313316
                    if role_2 == role_from.opposite() && role_3 == role_from {
411
12673
                        count += 1;
412
12673
                    } else {
413
3300643
                        break;
414
                    }
415
                }
416

            
417
3300643
                let finish = start + count;
418
3300643
                let vertex = Vertex {
419
3300643
                    size,
420
3300643
                    x: finish,
421
3300643
                    y: board_size_usize - 1,
422
3300643
                };
423
3300643
                let role = Role::from(self.get(&vertex));
424
3300643
                if count > 1
425
1052
                    && (role == role_from || vertex.on_restricted_square())
426
555
                    && (vertex_to
427
555
                        == &(Vertex {
428
555
                            size,
429
555
                            x: start - 1,
430
555
                            y: board_size_usize - 1,
431
555
                        })
432
465
                        || vertex_to
433
465
                            == &(Vertex {
434
465
                                size,
435
465
                                x: finish,
436
465
                                y: board_size_usize - 1,
437
465
                            }))
438
                {
439
497
                    for x_2 in start..finish {
440
497
                        let vertex = Vertex {
441
497
                            size,
442
497
                            x: x_2,
443
497
                            y: board_size_usize - 1,
444
497
                        };
445
497
                        if self.set_if_not_king(&vertex, Space::Empty) {
446
491
                            captures.push(vertex);
447
491
                        }
448
                    }
449
3300410
                }
450
8893089
            }
451
        }
452

            
453
        // top row
454
13413104
        for x_1 in 0..board_size_usize {
455
13413104
            let vertex_1 = Vertex { size, x: x_1, y: 0 };
456
13413104
            if Role::from(self.get(&vertex_1)) == role_from || vertex_1.on_restricted_square() {
457
4680246
                let mut count = 0;
458

            
459
4680246
                if x_1 == board_size_usize - 1 {
460
1219372
                    break;
461
3460874
                }
462
3460874
                let start = x_1 + 1;
463

            
464
3477213
                for x_2 in start..board_size_usize {
465
3477213
                    let vertex_2 = Vertex { size, x: x_2, y: 0 };
466
3477213
                    let vertex_3 = Vertex { size, x: x_2, y: 1 };
467
3477213
                    let role_2 = Role::from(self.get(&vertex_2));
468
3477213
                    let role_3 = Role::from(self.get(&vertex_3));
469
3477213
                    if role_2 == role_from.opposite() && role_3 == role_from {
470
16339
                        count += 1;
471
16339
                    } else {
472
3460874
                        break;
473
                    }
474
                }
475

            
476
3460874
                let finish = start + count;
477
3460874
                let vertex = Vertex {
478
3460874
                    size,
479
3460874
                    x: finish,
480
3460874
                    y: 0,
481
3460874
                };
482
3460874
                let role = Role::from(self.get(&vertex));
483
3460874
                if count > 1
484
1020
                    && (role == role_from || vertex.on_restricted_square())
485
362
                    && (vertex_to
486
362
                        == &(Vertex {
487
362
                            size,
488
362
                            x: start - 1,
489
362
                            y: 0,
490
362
                        })
491
255
                        || vertex_to
492
255
                            == &(Vertex {
493
255
                                size,
494
255
                                x: finish,
495
255
                                y: 0,
496
255
                            }))
497
                {
498
450
                    for x_2 in start..finish {
499
450
                        let vertex = Vertex { size, x: x_2, y: 0 };
500
450
                        if self.set_if_not_king(&vertex, Space::Empty) {
501
430
                            captures.push(vertex);
502
430
                        }
503
                    }
504
3460666
                }
505
8732858
            }
506
        }
507

            
508
        // left row
509
13413104
        for y_1 in 0..board_size_usize {
510
13413104
            let vertex_1 = Vertex { size, x: 0, y: y_1 };
511
13413104
            if Role::from(self.get(&vertex_1)) == role_from || vertex_1.on_restricted_square() {
512
4734069
                let mut count = 0;
513

            
514
4734069
                if y_1 == board_size_usize - 1 {
515
1219372
                    break;
516
3514697
                }
517
3514697
                let start = y_1 + 1;
518

            
519
3531067
                for y_2 in start..board_size_usize {
520
3531067
                    let vertex_2 = Vertex { size, x: 0, y: y_2 };
521
3531067
                    let vertex_3 = Vertex { size, x: 1, y: y_2 };
522
3531067
                    let role_2 = Role::from(self.get(&vertex_2));
523
3531067
                    let role_3 = Role::from(self.get(&vertex_3));
524
3531067
                    if role_2 == role_from.opposite() && role_3 == role_from {
525
16370
                        count += 1;
526
16370
                    } else {
527
3514697
                        break;
528
                    }
529
                }
530

            
531
3514697
                let finish = start + count;
532
3514697
                let vertex = Vertex {
533
3514697
                    size,
534
3514697
                    x: 0,
535
3514697
                    y: finish,
536
3514697
                };
537
3514697
                let role = Role::from(self.get(&vertex));
538
3514697
                if count > 1
539
1269
                    && (role == role_from || vertex.on_restricted_square())
540
709
                    && (vertex_to
541
709
                        == &(Vertex {
542
709
                            size,
543
709
                            x: 0,
544
709
                            y: start - 1,
545
709
                        })
546
633
                        || vertex_to
547
633
                            == &(Vertex {
548
633
                                size,
549
633
                                x: 0,
550
633
                                y: finish,
551
633
                            }))
552
                {
553
413
                    for y_2 in start..finish {
554
413
                        let vertex = Vertex { size, x: 0, y: y_2 };
555
413
                        if self.set_if_not_king(&vertex, Space::Empty) {
556
407
                            captures.push(vertex);
557
407
                        }
558
                    }
559
3514506
                }
560
8679035
            }
561
        }
562

            
563
        // right row
564
13413104
        for y_1 in 0..board_size_usize {
565
13413104
            let vertex_1 = Vertex {
566
13413104
                size,
567
13413104
                x: board_size_usize - 1,
568
13413104
                y: y_1,
569
13413104
            };
570
13413104
            if Role::from(self.get(&vertex_1)) == role_from || vertex_1.on_restricted_square() {
571
4683985
                let mut count = 0;
572

            
573
4683985
                if y_1 == board_size_usize - 1 {
574
1219372
                    break;
575
3464613
                }
576
3464613
                let start = y_1 + 1;
577

            
578
3480824
                for y_2 in start..board_size_usize {
579
3480824
                    let vertex_2 = Vertex {
580
3480824
                        size,
581
3480824
                        x: board_size_usize - 1,
582
3480824
                        y: y_2,
583
3480824
                    };
584
3480824
                    let vertex_3 = Vertex {
585
3480824
                        size,
586
3480824
                        x: board_size_usize - 2,
587
3480824
                        y: y_2,
588
3480824
                    };
589
3480824
                    let role_2 = Role::from(self.get(&vertex_2));
590
3480824
                    let role_3 = Role::from(self.get(&vertex_3));
591
3480824
                    if role_2 == role_from.opposite() && role_3 == role_from {
592
16211
                        count += 1;
593
16211
                    } else {
594
3464613
                        break;
595
                    }
596
                }
597

            
598
3464613
                let finish = start + count;
599
3464613
                let vertex = Vertex {
600
3464613
                    size,
601
3464613
                    x: board_size_usize - 1,
602
3464613
                    y: finish,
603
3464613
                };
604
3464613
                let role = Role::from(self.get(&vertex));
605
3464613
                if count > 1
606
2281
                    && (role == role_from || vertex.on_restricted_square())
607
362
                    && (vertex_to
608
362
                        == &(Vertex {
609
362
                            size,
610
362
                            x: board_size_usize - 1,
611
362
                            y: start - 1,
612
362
                        })
613
286
                        || vertex_to
614
286
                            == &(Vertex {
615
286
                                size,
616
286
                                x: board_size_usize - 1,
617
286
                                y: finish,
618
286
                            }))
619
                {
620
511
                    for y_2 in start..finish {
621
511
                        let vertex = Vertex {
622
511
                            size,
623
511
                            x: board_size_usize - 1,
624
511
                            y: y_2,
625
511
                        };
626
511
                        if self.set_if_not_king(&vertex, Space::Empty) {
627
505
                            captures.push(vertex);
628
505
                        }
629
                    }
630
3464380
                }
631
8729119
            }
632
        }
633
1219372
    }
634

            
635
    #[allow(clippy::unwrap_used)]
636
    #[must_use]
637
60
    fn closed_off_exit(&self, exit: Vertex) -> bool {
638
60
        let size = self.size();
639
60
        let board_size_usize: usize = size.into();
640

            
641
60
        let hasher = FxBuildHasher;
642
60
        let mut already_checked =
643
60
            FxHashSet::with_capacity_and_hasher(board_size_usize * board_size_usize, hasher);
644

            
645
60
        already_checked.insert(exit);
646

            
647
60
        let mut pre_stack = Vec::with_capacity(board_size_usize * board_size_usize);
648
60
        let up = expand_flood_fill(exit.up(), &mut already_checked, &mut pre_stack);
649
60
        let left = expand_flood_fill(exit.left(), &mut already_checked, &mut pre_stack);
650
60
        let down = expand_flood_fill(exit.down(), &mut already_checked, &mut pre_stack);
651
60
        let right = expand_flood_fill(exit.right(), &mut already_checked, &mut pre_stack);
652

            
653
60
        if up
654
30
            && left
655
15
            && self.get(&pre_stack[0]) == Space::Attacker
656
9
            && self.get(&pre_stack[1]) == Space::Attacker
657
9
            && self.get(&pre_stack[0].up().unwrap()) == Space::Attacker
658
6
            && self.get(&pre_stack[1].left().unwrap()) == Space::Attacker
659
        {
660
3
            return true;
661
57
        }
662

            
663
57
        if up
664
27
            && right
665
15
            && self.get(&pre_stack[0]) == Space::Attacker
666
9
            && self.get(&pre_stack[1]) == Space::Attacker
667
9
            && self.get(&pre_stack[0].up().unwrap()) == Space::Attacker
668
6
            && self.get(&pre_stack[1].right().unwrap()) == Space::Attacker
669
        {
670
3
            return true;
671
54
        }
672

            
673
54
        if left
674
27
            && down
675
15
            && self.get(&pre_stack[0]) == Space::Attacker
676
9
            && self.get(&pre_stack[1]) == Space::Attacker
677
9
            && self.get(&pre_stack[0].left().unwrap()) == Space::Attacker
678
6
            && self.get(&pre_stack[1].down().unwrap()) == Space::Attacker
679
        {
680
3
            return true;
681
51
        }
682

            
683
51
        if down
684
27
            && right
685
15
            && self.get(&pre_stack[0]) == Space::Attacker
686
9
            && self.get(&pre_stack[1]) == Space::Attacker
687
9
            && self.get(&pre_stack[0].down().unwrap()) == Space::Attacker
688
6
            && self.get(&pre_stack[1].right().unwrap()) == Space::Attacker
689
        {
690
3
            return true;
691
48
        }
692

            
693
48
        let mut stack = Vec::with_capacity(board_size_usize * board_size_usize);
694
96
        for vertex in pre_stack {
695
96
            let space = self.get(&vertex);
696
96
            if space == Space::Empty || space == Space::Attacker {
697
96
                let _ = expand_flood_fill(vertex.up(), &mut already_checked, &mut stack);
698
96
                let _ = expand_flood_fill(vertex.left(), &mut already_checked, &mut stack);
699
96
                let _ = expand_flood_fill(vertex.down(), &mut already_checked, &mut stack);
700
96
                let _ = expand_flood_fill(vertex.right(), &mut already_checked, &mut stack);
701
96
            }
702
        }
703

            
704
852
        while !stack.is_empty() {
705
837
            if let Some(vertex) = stack.pop() {
706
837
                let space = self.get(&vertex);
707
837
                if space == Space::Empty {
708
669
                    let _ = expand_flood_fill(vertex.up(), &mut already_checked, &mut stack);
709
669
                    let _ = expand_flood_fill(vertex.left(), &mut already_checked, &mut stack);
710
669
                    let _ = expand_flood_fill(vertex.down(), &mut already_checked, &mut stack);
711
669
                    let _ = expand_flood_fill(vertex.right(), &mut already_checked, &mut stack);
712
669
                } else if Into::<Role>::into(space) == Role::Defender {
713
33
                    return false;
714
135
                }
715
            }
716
        }
717

            
718
15
        true
719
60
    }
720

            
721
    #[must_use]
722
15
    pub fn closed_off_exits(&self) -> u8 {
723
15
        let mut closed_off_exits = 0;
724

            
725
60
        for exit in self.exit_squares() {
726
60
            if self.closed_off_exit(exit) {
727
27
                closed_off_exits += 1;
728
33
            }
729
        }
730

            
731
15
        closed_off_exits
732
15
    }
733

            
734
    #[must_use]
735
    pub fn difference(&self, other: &Board) -> Option<Plae> {
736
        let size = self.size();
737
        let size_usize = size.into();
738
        let mut role = None;
739
        let mut from = None;
740
        let mut to = None;
741

            
742
        for y in 0..size_usize {
743
            for x in 0..size_usize {
744
                let vertex = Vertex { size, x, y };
745
                let a = self.get(&vertex);
746
                let b = other.get(&vertex);
747
                if a != b {
748
                    if a == Space::Empty {
749
                        to = Some(vertex);
750
                        role = Some(b.into());
751
                    }
752
                    if b == Space::Empty {
753
                        from = Some(vertex);
754
                    }
755
                }
756
            }
757
        }
758

            
759
        if let (Some(role), Some(from), Some(to)) = (role, from, to) {
760
            Some(Plae::Play(Play { role, from, to }))
761
        } else {
762
            None
763
        }
764
    }
765

            
766
    #[must_use]
767
15
    pub fn exit_squares(&self) -> Vec<Vertex> {
768
15
        match self.size() {
769
15
            BoardSize::_11 => EXIT_SQUARES_11X11.into(),
770
            BoardSize::_13 => EXIT_SQUARES_13X13.into(),
771
        }
772
15
    }
773

            
774
    #[must_use]
775
4865233
    pub fn find_the_king(&self) -> Option<Vertex> {
776
4865233
        let size = self.size();
777
4865233
        let board_size_usize: usize = size.into();
778

            
779
28960484
        for y in 0..board_size_usize {
780
294377780
            for x in 0..board_size_usize {
781
294377780
                let v = Vertex { size, x, y };
782
294377780
                if self.get(&v) == Space::King {
783
4864487
                    return Some(v);
784
289513293
                }
785
            }
786
        }
787

            
788
746
        None
789
4865233
    }
790

            
791
    #[must_use]
792
1203594
    pub fn captured_the_king(&self) -> bool {
793
72854040
        for space in &self.spaces {
794
72854040
            if *space == Space::King {
795
1202992
                return false;
796
71651048
            }
797
        }
798

            
799
602
        true
800
1203594
    }
801

            
802
1216080
    fn capture_the_king(
803
1216080
        &mut self,
804
1216080
        role_from: Role,
805
1216080
        play_to: &Vertex,
806
1216080
        captures: &mut Vec<Vertex>,
807
1216080
    ) -> bool {
808
1216080
        if let Some(kings_vertex) = self.find_the_king()
809
1216032
            && role_from == Role::Attacker
810
        {
811
615098
            let mut attacker_moved = false;
812

            
813
615098
            let (move_to_capture, surrounded) =
814
615098
                self.capture_the_king_space(play_to, kings_vertex.up());
815

            
816
615098
            if !surrounded {
817
511647
                return false;
818
103451
            }
819
103451
            if move_to_capture {
820
18373
                attacker_moved = true;
821
85078
            }
822

            
823
103451
            let (move_to_capture, surrounded) =
824
103451
                self.capture_the_king_space(play_to, kings_vertex.left());
825

            
826
103451
            if !surrounded {
827
79554
                return false;
828
23897
            }
829
23897
            if move_to_capture {
830
4320
                attacker_moved = true;
831
19577
            }
832

            
833
23897
            let (move_to_capture, surrounded) =
834
23897
                self.capture_the_king_space(play_to, kings_vertex.down());
835

            
836
23897
            if !surrounded {
837
19568
                return false;
838
4329
            }
839
4329
            if move_to_capture {
840
791
                attacker_moved = true;
841
3538
            }
842

            
843
4329
            let (move_to_capture, surrounded) =
844
4329
                self.capture_the_king_space(play_to, kings_vertex.right());
845

            
846
4329
            if !surrounded {
847
3655
                return false;
848
674
            }
849
674
            if move_to_capture {
850
71
                attacker_moved = true;
851
603
            }
852

            
853
674
            if attacker_moved {
854
618
                self.set(&kings_vertex, Space::Empty);
855
618
                captures.push(kings_vertex);
856
618
                return true;
857
56
            }
858
600982
        }
859

            
860
601038
        false
861
1216080
    }
862

            
863
    #[must_use]
864
7618
    pub fn capture_the_king_one_move(&self) -> Option<Vertex> {
865
7618
        let mut spaces_left = 4;
866
7618
        let mut capture = None;
867

            
868
7618
        if let Some(kings_vertex) = self.find_the_king() {
869
7618
            if let Some(vertex) = kings_vertex.up() {
870
7580
                if vertex.on_throne() || self.get(&vertex) == Space::Attacker {
871
506
                    spaces_left -= 1;
872
7074
                } else {
873
7074
                    capture = Some(vertex);
874
7074
                }
875
38
            }
876

            
877
7618
            if let Some(vertex) = kings_vertex.left() {
878
7591
                if vertex.on_throne() || self.get(&vertex) == Space::Attacker {
879
442
                    spaces_left -= 1;
880
7149
                } else {
881
7149
                    capture = Some(vertex);
882
7149
                }
883
27
            }
884

            
885
7618
            if let Some(vertex) = kings_vertex.down() {
886
7612
                if vertex.on_throne() || self.get(&vertex) == Space::Attacker {
887
532
                    spaces_left -= 1;
888
7080
                } else {
889
7080
                    capture = Some(vertex);
890
7080
                }
891
6
            }
892

            
893
7618
            if let Some(vertex) = kings_vertex.right() {
894
7609
                if vertex.on_throne() || self.get(&vertex) == Space::Attacker {
895
522
                    spaces_left -= 1;
896
7087
                } else {
897
7087
                    capture = Some(vertex);
898
7087
                }
899
9
            }
900
        }
901

            
902
7618
        if spaces_left == 1 { capture } else { None }
903
7618
    }
904

            
905
    #[inline]
906
746775
    fn capture_the_king_space(&self, play_to: &Vertex, direction: Option<Vertex>) -> (bool, bool) {
907
746775
        if let Some(surround_king) = direction {
908
717597
            let move_to_capture = *play_to == surround_king;
909

            
910
717597
            let surrounded = move_to_capture
911
694042
                || surround_king.on_throne()
912
689467
                || self.get(&surround_king) == Space::Attacker;
913

            
914
717597
            (move_to_capture, surrounded)
915
        } else {
916
29178
            (false, false)
917
        }
918
746775
    }
919

            
920
1215462
    fn exit_forts(&self) -> bool {
921
1215462
        match self.find_the_king() {
922
1215414
            Some(kings_vertex) => {
923
1215414
                kings_vertex.touches_wall()
924
141798
                    && self.able_to_move(&kings_vertex)
925
125188
                    && self.flood_fill_defender_wins(&kings_vertex)
926
            }
927
48
            None => false,
928
        }
929
1215462
    }
930

            
931
    #[inline]
932
1214853
    fn flood_fill_attacker_wins(&self) -> bool {
933
1214853
        let size = self.size();
934
1214853
        let board_size_usize: usize = size.into();
935

            
936
1214853
        match self.find_the_king() {
937
1214805
            Some(kings_vertex) => {
938
1214805
                let hasher = FxBuildHasher;
939
1214805
                let mut already_checked = FxHashSet::with_capacity_and_hasher(
940
1214805
                    board_size_usize * board_size_usize,
941
1214805
                    hasher,
942
                );
943

            
944
1214805
                already_checked.insert(kings_vertex);
945
1214805
                let mut stack = Vec::with_capacity(board_size_usize * board_size_usize);
946
1214805
                stack.push(kings_vertex);
947

            
948
11557554
                while !stack.is_empty() {
949
11557100
                    if let Some(vertex) = stack.pop() {
950
11557100
                        let space = self.get(&vertex);
951
11557100
                        if space == Space::Empty || Role::from(space) == Role::Defender {
952
9002932
                            if !expand_flood_fill(vertex.up(), &mut already_checked, &mut stack) {
953
42810
                                return false;
954
8960122
                            }
955
8960122
                            if !expand_flood_fill(vertex.left(), &mut already_checked, &mut stack) {
956
48790
                                return false;
957
8911332
                            }
958
8911332
                            if !expand_flood_fill(vertex.down(), &mut already_checked, &mut stack) {
959
123289
                                return false;
960
8788043
                            }
961
8788043
                            if !expand_flood_fill(vertex.right(), &mut already_checked, &mut stack)
962
                            {
963
999462
                                return false;
964
7788581
                            }
965
2554168
                        }
966
                    }
967
                }
968

            
969
4113
                for y in 0..board_size_usize {
970
44886
                    for x in 0..board_size_usize {
971
44886
                        let vertex = Vertex { size, x, y };
972
44886
                        if Role::from(self.get(&vertex)) == Role::Defender
973
2149
                            && !already_checked.contains(&vertex)
974
                        {
975
129
                            return false;
976
44757
                        }
977
                    }
978
                }
979

            
980
325
                true
981
            }
982
48
            None => false,
983
        }
984
1214853
    }
985

            
986
    #[must_use]
987
    #[allow(clippy::too_many_lines)]
988
125194
    pub fn flood_fill_defender_wins(&self, vertex: &Vertex) -> bool {
989
125194
        let size = self.size();
990
125194
        let board_size_usize = size.into();
991

            
992
125194
        let mut attacker_has_enough_pieces = false;
993
125194
        let mut count = 0;
994
148228
        'outer: for y in 0..board_size_usize {
995
1004968
            for x in 0..board_size_usize {
996
1004968
                let vertex = Vertex { size, x, y };
997
1004968
                if Role::from(self.get(&vertex)) == Role::Attacker {
998
250379
                    count += 1;
999
754589
                }
1004968
                if count > 1 {
125185
                    attacker_has_enough_pieces = true;
125185
                    break 'outer;
879783
                }
            }
        }
125194
        let mut already_checked = FxHashSet::default();
125194
        let mut stack = vec![];
125194
        if let Some(vertex) = vertex.up() {
95578
            stack.push((vertex, Direction::LeftRight));
95583
        }
125194
        if let Some(vertex) = vertex.left() {
92660
            stack.push((vertex, Direction::UpDown));
92660
        }
125194
        if let Some(vertex) = vertex.down() {
93000
            stack.push((vertex, Direction::LeftRight));
93000
        }
125194
        if let Some(vertex) = vertex.right() {
94344
            stack.push((vertex, Direction::UpDown));
94344
        }
486150
        while !stack.is_empty() {
485538
            if let Some((vertex, direction)) = stack.pop() {
485538
                let space = self.get(&vertex);
485538
                if space == Space::Empty {
313227
                    if let Some(vertex) = vertex.up()
290436
                        && !already_checked.contains(&vertex)
247009
                    {
247009
                        stack.push((vertex, Direction::LeftRight));
247009
                        already_checked.insert(vertex);
247009
                    }
313227
                    if let Some(vertex) = vertex.left()
311847
                        && !already_checked.contains(&vertex)
165155
                    {
165155
                        stack.push((vertex, Direction::UpDown));
165155
                        already_checked.insert(vertex);
165155
                    }
313227
                    if let Some(vertex) = vertex.down()
271616
                        && !already_checked.contains(&vertex)
253810
                    {
253810
                        stack.push((vertex, Direction::LeftRight));
253810
                        already_checked.insert(vertex);
253863
                    }
313227
                    if let Some(vertex) = vertex.right()
253486
                        && !already_checked.contains(&vertex)
228038
                    {
228038
                        stack.push((vertex, Direction::UpDown));
228038
                        already_checked.insert(vertex);
228053
                    }
172311
                } else if Role::from(space) == Role::Attacker {
113282
                    return false;
59029
                } else if direction == Direction::UpDown {
41590
                    let mut vertex_1 = false;
41590
                    let mut vertex_2 = false;
41590
                    if let Some(vertex) = vertex.up() {
34200
                        if Role::from(self.get(&vertex)) == Role::Defender {
9153
                            vertex_1 = true;
25071
                        }
7390
                    } else {
7390
                        vertex_1 = true;
7390
                    }
41590
                    if let Some(vertex) = vertex.down() {
29377
                        if Role::from(self.get(&vertex)) == Role::Defender {
9071
                            vertex_2 = true;
20341
                        }
12213
                    } else {
12213
                        vertex_2 = true;
12213
                    }
41590
                    if !vertex_1 && !vertex_2 && attacker_has_enough_pieces {
8908
                        return false;
32682
                    }
                } else {
17439
                    let mut vertex_1 = false;
17439
                    let mut vertex_2 = false;
17439
                    if let Some(vertex) = vertex.right() {
8931
                        if Role::from(self.get(&vertex)) == Role::Defender {
4788
                            vertex_1 = true;
4788
                        }
8508
                    } else {
8508
                        vertex_1 = true;
8508
                    }
17439
                    if let Some(vertex) = vertex.left() {
16747
                        if Role::from(self.get(&vertex)) == Role::Defender {
4797
                            vertex_2 = true;
11957
                        }
692
                    } else {
692
                        vertex_2 = true;
692
                    }
17439
                    if !vertex_1 && !vertex_2 && attacker_has_enough_pieces {
2392
                        return false;
15047
                    }
                }
            }
        }
612
        true
125194
    }
    #[must_use]
472241826
    pub fn get(&self, vertex: &Vertex) -> Space {
472241826
        let board_size: usize = self.size().into();
472241826
        self.spaces[vertex.y * board_size + vertex.x]
472241826
    }
    #[must_use]
    pub fn get_neighbors(
        &self,
        starts: &Vec<Vertex>,
        visited: &HashMap<Vertex, (u8, Option<Vertex>)>,
    ) -> Vec<Vertex> {
        let mut neighbors = Vec::new();
        let size = self.size();
        let board_usize = size.into();
        for start in starts {
            for x in 1..=start.x {
                let index = start.x - x;
                let vertex = Vertex {
                    size,
                    x: index,
                    y: start.y,
                };
                if self.get(&vertex) != Space::Empty {
                    break;
                }
                if !visited.contains_key(&vertex) {
                    neighbors.push(vertex);
                }
            }
            for x in (start.x + 1)..board_usize {
                let vertex = Vertex {
                    size,
                    x,
                    y: start.y,
                };
                if self.get(&vertex) != Space::Empty {
                    break;
                }
                if !visited.contains_key(&vertex) {
                    neighbors.push(vertex);
                }
            }
            for y in 1..=start.y {
                let index = start.y - y;
                let vertex = Vertex {
                    size,
                    x: start.x,
                    y: index,
                };
                if self.get(&vertex) != Space::Empty {
                    break;
                }
                if !visited.contains_key(&vertex) {
                    neighbors.push(vertex);
                }
            }
            for y in (start.y + 1)..board_usize {
                let vertex = Vertex {
                    size,
                    x: start.x,
                    y,
                };
                if self.get(&vertex) != Space::Empty {
                    break;
                }
                if !visited.contains_key(&vertex) {
                    neighbors.push(vertex);
                }
            }
        }
        neighbors
    }
    #[must_use]
502060934
    pub fn size(&self) -> BoardSize {
502060934
        let len = self.spaces.len();
502060934
        if len == 11 * 11 {
502058114
            BoardSize::_11
2820
        } else if len == 13 * 13 {
2820
            BoardSize::_13
        } else {
            unreachable!()
        }
502060934
    }
    /// # Errors
    ///
    /// If the move is illegal.
    #[allow(
        clippy::cast_possible_truncation,
        clippy::cast_possible_wrap,
        clippy::cast_sign_loss
    )]
11457429
    pub fn legal_move(
11457429
        &self,
11457429
        play: &Play,
11457429
        status: &Status,
11457429
        turn: &Role,
11457429
        previous_boards: &PreviousBoards,
11457429
    ) -> Result<Board, InvalidMove> {
11457429
        let size = self.size();
11457429
        if *status != Status::Ongoing {
            return Err(InvalidMove::GameOver);
11457429
        }
11457429
        let space_from = self.get(&play.from);
11457429
        let role_from = Role::from(space_from);
11457429
        if role_from == Role::Roleless {
            return Err(InvalidMove::Role);
11457429
        } else if *turn != role_from {
            return Err(InvalidMove::Turn);
11457429
        }
11457429
        let x_diff = play.from.x as i32 - play.to.x as i32;
11457429
        let y_diff = play.from.y as i32 - play.to.y as i32;
11457429
        if x_diff != 0 && y_diff != 0 {
27
            return Err(InvalidMove::StraightLine);
11457402
        }
11457402
        if x_diff == 0 && y_diff == 0 {
713333
            return Err(InvalidMove::Location);
10744069
        }
10744069
        if x_diff != 0 {
6018087
            let x_diff_sign = x_diff.signum();
13133261
            for x_diff in 1..=x_diff.abs() {
13133261
                let vertex = Vertex {
13133261
                    size,
13133261
                    x: (play.from.x as i32 - (x_diff * x_diff_sign)) as usize,
13133261
                    y: play.from.y,
13133261
                };
13133261
                let space = self.get(&vertex);
13133261
                if space != Space::Empty {
3170110
                    return Err(InvalidMove::Empty);
9963151
                }
            }
        } else {
4725982
            let y_diff_sign = y_diff.signum();
9699761
            for y_diff in 1..=y_diff.abs() {
9699761
                let vertex = Vertex {
9699761
                    size,
9699761
                    x: play.from.x,
9699761
                    y: (play.from.y as i32 - (y_diff * y_diff_sign)) as usize,
9699761
                };
9699761
                let space = self.get(&vertex);
9699761
                if space != Space::Empty {
2741215
                    return Err(InvalidMove::Empty);
6958546
                }
            }
        }
4832744
        if space_from != Space::King && play.to.on_restricted_square() {
683080
            return Err(InvalidMove::Restricted);
4149664
        }
4149664
        let mut board = self.clone();
4149664
        board.set(&play.from, Space::Empty);
4149664
        board.set(&play.to, space_from);
4149664
        if turn == &Role::Defender && previous_boards.0.contains(&board) {
1400
            return Err(InvalidMove::RepeatMove);
4148264
        }
4148264
        Ok(board)
11457429
    }
    #[must_use]
1214528
    fn no_attacker_pieces_left(&self) -> bool {
1214528
        let size = self.size();
1214528
        let board_size_usize: usize = size.into();
1242319
        for y in 0..board_size_usize {
5437363
            for x in 0..board_size_usize {
5437363
                let v = Vertex { size, x, y };
5437363
                if Role::from(self.get(&v)) == Role::Attacker {
1214492
                    return false;
4222871
                }
            }
        }
36
        true
1214528
    }
    /// # Errors
    ///
    /// If the move is illegal.
1219930
    pub fn play(
1219930
        &mut self,
1219930
        play: &Plae,
1219930
        status: &Status,
1219930
        turn: &Role,
1219930
        previous_boards: &mut PreviousBoards,
1219930
    ) -> anyhow::Result<(Vec<Vertex>, Status)> {
1219930
        let (board, captures, status) = self.play_internal(play, status, turn, previous_boards)?;
1219372
        previous_boards.0.insert(board.clone());
1219372
        *self = board;
1219372
        Ok((captures, status))
1219930
    }
    /// # Errors
    ///
    /// If the move is illegal.
1219930
    pub fn play_internal(
1219930
        &self,
1219930
        play: &Plae,
1219930
        status: &Status,
1219930
        turn: &Role,
1219930
        previous_boards: &PreviousBoards,
1219930
    ) -> anyhow::Result<(Board, Vec<Vertex>, Status)> {
1219930
        if *status != Status::Ongoing {
            return Err(anyhow::Error::msg(
                "play: the game has to be ongoing to play",
            ));
1219930
        }
1219930
        let play = match play {
            Plae::AttackerResigns => return Ok((self.clone(), Vec::new(), Status::DefenderWins)),
            Plae::DefenderResigns => return Ok((self.clone(), Vec::new(), Status::AttackerWins)),
1219930
            Plae::Play(play) => play,
        };
1219930
        let mut board = self.legal_move(play, status, turn, previous_boards)?;
1219372
        let space_from = self.get(&play.from);
1219372
        let role_from = Role::from(space_from);
1219372
        let mut captures = Vec::new();
1219372
        board.captures(&play.to, role_from, &mut captures);
1219372
        board.captures_shield_wall(role_from, &play.to, &mut captures);
1219372
        if play.to.on_exit_square() {
3292
            return Ok((board, captures, Status::DefenderWins));
1216080
        }
1216080
        if board.capture_the_king(role_from, &play.to, &mut captures) {
618
            return Ok((board, captures, Status::AttackerWins));
1215462
        }
1215462
        if board.exit_forts() {
609
            return Ok((board, captures, Status::DefenderWins));
1214853
        }
1214853
        if board.flood_fill_attacker_wins() {
325
            return Ok((board, captures, Status::AttackerWins));
1214528
        }
1214528
        if board.no_attacker_pieces_left() {
36
            return Ok((board, captures, Status::DefenderWins));
1214492
        }
1214492
        Ok((board, captures, Status::Ongoing))
1219930
    }
8484356
    fn set(&mut self, vertex: &Vertex, space: Space) {
8484356
        let board_size: usize = self.size().into();
8484356
        self.spaces[vertex.y * board_size + vertex.x] = space;
8484356
    }
    #[must_use]
184448
    fn set_if_not_king(&mut self, vertex: &Vertex, space: Space) -> bool {
184448
        if self.get(vertex) == Space::King {
38
            false
        } else {
184410
            self.set(vertex, space);
184410
            true
        }
184448
    }
    #[must_use]
    pub fn spaces_around_the_king(&self) -> Option<u8> {
        let king = self.find_the_king()?;
        let Some(up) = king.up() else {
            return Some(5);
        };
        let Some(left) = king.left() else {
            return Some(5);
        };
        let Some(down) = king.down() else {
            return Some(5);
        };
        let Some(right) = king.right() else {
            return Some(5);
        };
        let mut sum = 4;
        for vertex in [up, left, down, right] {
            if self.get(&vertex) == Space::Attacker || vertex.on_throne() {
                sum -= 1;
            }
        }
        Some(sum)
    }
}
#[derive(
    Clone, Copy, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize,
)]
pub enum BoardSize {
    #[default]
    _11,
    _13,
}
impl fmt::Display for BoardSize {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            BoardSize::_11 => write!(f, "11"),
            BoardSize::_13 => write!(f, "13"),
        }
    }
}
impl From<BoardSize> for usize {
513373291
    fn from(size: BoardSize) -> Self {
513373291
        match size {
513370417
            BoardSize::_11 => 11,
2874
            BoardSize::_13 => 13,
        }
513373291
    }
}
impl FromStr for BoardSize {
    type Err = anyhow::Error;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "11" => Ok(BoardSize::_11),
            "13" => Ok(BoardSize::_13),
            _ => Err(anyhow::Error::msg(format!("expected 11 or 13, got {s}"))),
        }
    }
}
impl TryFrom<usize> for BoardSize {
    type Error = anyhow::Error;
2633070
    fn try_from(value: usize) -> Result<Self, Self::Error> {
2633070
        match value {
2633058
            11 => Ok(BoardSize::_11),
12
            13 => Ok(BoardSize::_13),
            _ => Err(anyhow::Error::msg(format!(
                "an invalid board size was passed: {value}"
            ))),
        }
2633070
    }
}
#[must_use]
#[allow(clippy::missing_panics_doc)]
#[allow(clippy::unwrap_used)]
49416
fn board_11x11() -> Board {
49416
    let spaces: Vec<Space> = STARTING_POSITION_11X11
49416
        .iter()
5979336
        .flat_map(|space| space.chars().map(|ch| ch.try_into().unwrap()))
49416
        .collect();
49416
    Board {
49416
        spaces,
49416
        display_ascii: false,
49416
    }
49416
}
#[must_use]
#[allow(clippy::missing_panics_doc)]
#[allow(clippy::unwrap_used)]
fn board_13x13() -> Board {
    let spaces: Vec<Space> = STARTING_POSITION_13X13
        .iter()
        .flat_map(|space| space.chars().map(|ch| ch.try_into().unwrap()))
        .collect();
    Board {
        spaces,
        display_ascii: false,
    }
}
pub struct Captured {
    pub attacker: u8,
    pub defender: u8,
    pub king: bool,
}
impl Captured {
    #[must_use]
    pub fn attacker(&self, chars: &Characters) -> String {
        format!("{} {}", chars.attacker, self.attacker)
    }
    #[must_use]
    pub fn defender(&self, chars: &Characters) -> String {
        let mut string = format!("{} {}", chars.defender, self.defender);
        if self.king {
            string.push(' ');
            string.push_str(&chars.king);
        }
        string
    }
}
#[derive(Clone, Debug, Eq, PartialEq)]
enum Direction {
    LeftRight,
    UpDown,
}
#[derive(Error, Debug)]
pub enum InvalidMove {
    #[error("play: the game is already over")]
    GameOver,
    #[error("play: you have to play through empty locations")]
    Empty,
    #[error("play: you have to change location")]
    Location,
    #[error("play: you already reached that position")]
    RepeatMove,
    #[error("play: only the king may move to a restricted square")]
    Restricted,
    #[error("play: you didn't select a role")]
    Role,
    #[error("play: you can only play in a straight line")]
    StraightLine,
    #[error("play: it isn't your turn")]
    Turn,
}
#[must_use]
#[inline]
35665729
fn expand_flood_fill(
35665729
    vertex: Option<Vertex>,
35665729
    already_checked: &mut FxHashSet<Vertex>,
35665729
    stack: &mut Vec<Vertex>,
35665729
) -> bool {
35665729
    if let Some(vertex) = vertex {
34450823
        if !already_checked.contains(&vertex) {
23440622
            stack.push(vertex);
23440622
            already_checked.insert(vertex);
23440622
        }
34450823
        true
    } else {
1214906
        false
    }
35665729
}