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
use std::{collections::HashMap, fmt, str::FromStr};
17

            
18
use rustc_hash::{FxBuildHasher, FxHashSet};
19
use serde::{Deserialize, Serialize};
20
use thiserror::Error;
21

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

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

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

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

            
68
impl Default for Board {
69
61674
    fn default() -> Self {
70
61674
        board_11x11()
71
61674
    }
72
}
73

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

            
78
        writeln!(f)?;
79
        for y in 0..board_size {
80
            write!(f, r#"""#)?;
81

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

            
93
        Ok(())
94
    }
95
}
96

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

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

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

            
142
impl TryFrom<[&str; 11]> for Board {
143
    type Error = anyhow::Error;
144

            
145
558
    fn try_from(value: [&str; 11]) -> anyhow::Result<Self> {
146
558
        let mut spaces = Vec::with_capacity(11 * 11);
147
558
        let mut kings = 0;
148

            
149
5928
        for (y, row) in value.iter().enumerate() {
150
65004
            for (x, ch) in row.chars().enumerate() {
151
65004
                let space = ch.try_into()?;
152
65004
                match space {
153
                    Space::Attacker | Space::Defender => {
154
2646
                        let vertex = Vertex {
155
2646
                            size: BoardSize::_11,
156
2646
                            x,
157
2646
                            y,
158
2646
                        };
159
2646
                        if vertex.on_restricted_square() {
160
30
                            return Err(anyhow::Error::msg(
161
30
                                "Only the king is allowed on restricted squares!",
162
30
                            ));
163
2616
                        }
164
                    }
165
61974
                    Space::Empty => {}
166
                    Space::King => {
167
384
                        kings += 1;
168
384
                        if kings > 1 {
169
6
                            return Err(anyhow::Error::msg("You can only have one king!"));
170
378
                        }
171
                    }
172
                }
173

            
174
64968
                spaces.push(space);
175
            }
176
        }
177

            
178
522
        Ok(Self {
179
522
            spaces,
180
522
            display_ascii: false,
181
522
        })
182
558
    }
183
}
184

            
185
impl TryFrom<[&str; 13]> for Board {
186
    type Error = anyhow::Error;
187

            
188
18
    fn try_from(value: [&str; 13]) -> anyhow::Result<Self> {
189
18
        let mut spaces = Vec::with_capacity(13 * 13);
190
18
        let mut kings = 0;
191

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

            
217
3042
                spaces.push(space);
218
            }
219
        }
220

            
221
18
        Ok(Self {
222
18
            spaces,
223
18
            display_ascii: false,
224
18
        })
225
18
    }
226
}
227

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

            
237
354248
    fn able_to_move(&self, play_from: &Vertex) -> bool {
238
354248
        if let Some(vertex) = play_from.up()
239
272508
            && self.get(&vertex) == Space::Empty
240
        {
241
143454
            return true;
242
210794
        }
243

            
244
210794
        if let Some(vertex) = play_from.left()
245
154851
            && self.get(&vertex) == Space::Empty
246
        {
247
78519
            return true;
248
132275
        }
249

            
250
132275
        if let Some(vertex) = play_from.down()
251
123904
            && self.get(&vertex) == Space::Empty
252
        {
253
63985
            return true;
254
68290
        }
255

            
256
68290
        if let Some(vertex) = play_from.right()
257
54045
            && self.get(&vertex) == Space::Empty
258
        {
259
26768
            return true;
260
41522
        }
261

            
262
41522
        false
263
354248
    }
264

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

            
275
6026734
        for y in 0..board_size_usize {
276
48046221
            for x in 0..board_size_usize {
277
48046221
                let vertex_from = Vertex { size, x, y };
278
48046221
                if Role::from(self.get(&vertex_from)) != *turn {
279
44981568
                    continue;
280
3064653
                }
281

            
282
5095545
                for y in 0..board_size_usize {
283
34111099
                    for x in 0..board_size_usize {
284
34111099
                        let vertex_to = Vertex { size, x, y };
285

            
286
34111099
                        if vertex_to.x != vertex_from.x && vertex_to.y != vertex_from.y {
287
24422881
                            continue;
288
9688218
                        }
289

            
290
9688218
                        let play = Play {
291
9688218
                            role: *turn,
292
9688218
                            from: vertex_from,
293
9688218
                            to: vertex_to,
294
9688218
                        };
295

            
296
9688218
                        if let Ok(_board) = self.legal_move(&play, status, turn, previous_boards) {
297
3026936
                            return true;
298
6661282
                        }
299
                    }
300
                }
301
            }
302
        }
303

            
304
748
        false
305
3027684
    }
306

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

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

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

            
333
        Captured {
334
            attacker,
335
            defender,
336
            king,
337
        }
338
    }
339

            
340
3039818
    fn captures(&mut self, play_to: &Vertex, role_from: Role, captures: &mut Vec<Vertex>) {
341
3039818
        self.captures_(play_to, role_from, captures, super::play::Vertex::up);
342
3039818
        self.captures_(play_to, role_from, captures, super::play::Vertex::left);
343
3039818
        self.captures_(play_to, role_from, captures, super::play::Vertex::down);
344
3039818
        self.captures_(play_to, role_from, captures, super::play::Vertex::right);
345
3039818
    }
346

            
347
12159272
    fn captures_<T: Fn(&Vertex) -> Option<Vertex>>(
348
12159272
        &mut self,
349
12159272
        play_to: &Vertex,
350
12159272
        role_from: Role,
351
12159272
        captures: &mut Vec<Vertex>,
352
12159272
        over: T,
353
12159272
    ) {
354
12159272
        if let Some(right_1) = over(play_to) {
355
11524795
            let space = self.get(&right_1);
356
11524795
            if space != Space::King
357
11322497
                && Role::from(space) == role_from.opposite()
358
1762729
                && let Some(right_2) = over(&right_1)
359
1480591
                && ((right_2.on_restricted_square() && self.get(&right_2) != Space::King)
360
1437627
                    || Role::from(self.get(&right_2)) == role_from)
361
456190
                && self.set_if_not_king(&right_1, Space::Empty)
362
456190
            {
363
456190
                captures.push(right_1);
364
11068605
            }
365
634477
        }
366
12159272
    }
367

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

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

            
389
11265403
                if x_1 == board_size_usize - 1 {
390
3039818
                    break;
391
8225585
                }
392
8225585
                let start = x_1 + 1;
393

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

            
414
8225585
                let finish = start + count;
415
8225585
                let vertex = Vertex {
416
8225585
                    size,
417
8225585
                    x: finish,
418
8225585
                    y: board_size_usize - 1,
419
8225585
                };
420
8225585
                let role = Role::from(self.get(&vertex));
421
8225585
                if count > 1
422
2608
                    && (role == role_from || vertex.on_restricted_square())
423
1383
                    && (vertex_to
424
1383
                        == &(Vertex {
425
1383
                            size,
426
1383
                            x: start - 1,
427
1383
                            y: board_size_usize - 1,
428
1383
                        })
429
1161
                        || vertex_to
430
1161
                            == &(Vertex {
431
1161
                                size,
432
1161
                                x: finish,
433
1161
                                y: board_size_usize - 1,
434
1161
                            }))
435
                {
436
1232
                    for x_2 in start..finish {
437
1232
                        let vertex = Vertex {
438
1232
                            size,
439
1232
                            x: x_2,
440
1232
                            y: board_size_usize - 1,
441
1232
                        };
442
1232
                        if self.set_if_not_king(&vertex, Space::Empty) {
443
1220
                            captures.push(vertex);
444
1220
                        }
445
                    }
446
8225007
                }
447
22172619
            }
448
        }
449

            
450
        // top row
451
33438022
        for x_1 in 0..board_size_usize {
452
33438022
            let vertex_1 = Vertex { size, x: x_1, y: 0 };
453
33438022
            if Role::from(self.get(&vertex_1)) == role_from || vertex_1.on_restricted_square() {
454
11668004
                let mut count = 0;
455

            
456
11668004
                if x_1 == board_size_usize - 1 {
457
3039818
                    break;
458
8628186
                }
459
8628186
                let start = x_1 + 1;
460

            
461
8669078
                for x_2 in start..board_size_usize {
462
8669078
                    let vertex_2 = Vertex { size, x: x_2, y: 0 };
463
8669078
                    let vertex_3 = Vertex { size, x: x_2, y: 1 };
464
8669078
                    let role_2 = Role::from(self.get(&vertex_2));
465
8669078
                    let role_3 = Role::from(self.get(&vertex_3));
466
8669078
                    if role_2 == role_from.opposite() && role_3 == role_from {
467
40892
                        count += 1;
468
40892
                    } else {
469
8628186
                        break;
470
                    }
471
                }
472

            
473
8628186
                let finish = start + count;
474
8628186
                let vertex = Vertex {
475
8628186
                    size,
476
8628186
                    x: finish,
477
8628186
                    y: 0,
478
8628186
                };
479
8628186
                let role = Role::from(self.get(&vertex));
480
8628186
                if count > 1
481
2545
                    && (role == role_from || vertex.on_restricted_square())
482
899
                    && (vertex_to
483
899
                        == &(Vertex {
484
899
                            size,
485
899
                            x: start - 1,
486
899
                            y: 0,
487
899
                        })
488
636
                        || vertex_to
489
636
                            == &(Vertex {
490
636
                                size,
491
636
                                x: finish,
492
636
                                y: 0,
493
636
                            }))
494
                {
495
1110
                    for x_2 in start..finish {
496
1110
                        let vertex = Vertex { size, x: x_2, y: 0 };
497
1110
                        if self.set_if_not_king(&vertex, Space::Empty) {
498
1063
                            captures.push(vertex);
499
1063
                        }
500
                    }
501
8627672
                }
502
21770018
            }
503
        }
504

            
505
        // left row
506
33438022
        for y_1 in 0..board_size_usize {
507
33438022
            let vertex_1 = Vertex { size, x: 0, y: y_1 };
508
33438022
            if Role::from(self.get(&vertex_1)) == role_from || vertex_1.on_restricted_square() {
509
11801205
                let mut count = 0;
510

            
511
11801205
                if y_1 == board_size_usize - 1 {
512
3039818
                    break;
513
8761387
                }
514
8761387
                let start = y_1 + 1;
515

            
516
8802282
                for y_2 in start..board_size_usize {
517
8802282
                    let vertex_2 = Vertex { size, x: 0, y: y_2 };
518
8802282
                    let vertex_3 = Vertex { size, x: 1, y: y_2 };
519
8802282
                    let role_2 = Role::from(self.get(&vertex_2));
520
8802282
                    let role_3 = Role::from(self.get(&vertex_3));
521
8802282
                    if role_2 == role_from.opposite() && role_3 == role_from {
522
40895
                        count += 1;
523
40895
                    } else {
524
8761387
                        break;
525
                    }
526
                }
527

            
528
8761387
                let finish = start + count;
529
8761387
                let vertex = Vertex {
530
8761387
                    size,
531
8761387
                    x: 0,
532
8761387
                    y: finish,
533
8761387
                };
534
8761387
                let role = Role::from(self.get(&vertex));
535
8761387
                if count > 1
536
3168
                    && (role == role_from || vertex.on_restricted_square())
537
1768
                    && (vertex_to
538
1768
                        == &(Vertex {
539
1768
                            size,
540
1768
                            x: 0,
541
1768
                            y: start - 1,
542
1768
                        })
543
1581
                        || vertex_to
544
1581
                            == &(Vertex {
545
1581
                                size,
546
1581
                                x: 0,
547
1581
                                y: finish,
548
1581
                            }))
549
                {
550
1022
                    for y_2 in start..finish {
551
1022
                        let vertex = Vertex { size, x: 0, y: y_2 };
552
1022
                        if self.set_if_not_king(&vertex, Space::Empty) {
553
1010
                            captures.push(vertex);
554
1010
                        }
555
                    }
556
8760914
                }
557
21636817
            }
558
        }
559

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

            
570
11675829
                if y_1 == board_size_usize - 1 {
571
3039818
                    break;
572
8636011
                }
573
8636011
                let start = y_1 + 1;
574

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

            
595
8636011
                let finish = start + count;
596
8636011
                let vertex = Vertex {
597
8636011
                    size,
598
8636011
                    x: board_size_usize - 1,
599
8636011
                    y: finish,
600
8636011
                };
601
8636011
                let role = Role::from(self.get(&vertex));
602
8636011
                if count > 1
603
5695
                    && (role == role_from || vertex.on_restricted_square())
604
899
                    && (vertex_to
605
899
                        == &(Vertex {
606
899
                            size,
607
899
                            x: board_size_usize - 1,
608
899
                            y: start - 1,
609
899
                        })
610
712
                        || vertex_to
611
712
                            == &(Vertex {
612
712
                                size,
613
712
                                x: board_size_usize - 1,
614
712
                                y: finish,
615
712
                            }))
616
                {
617
1267
                    for y_2 in start..finish {
618
1267
                        let vertex = Vertex {
619
1267
                            size,
620
1267
                            x: board_size_usize - 1,
621
1267
                            y: y_2,
622
1267
                        };
623
1267
                        if self.set_if_not_king(&vertex, Space::Empty) {
624
1255
                            captures.push(vertex);
625
1255
                        }
626
                    }
627
8635433
                }
628
21762193
            }
629
        }
630
3039818
    }
631

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

            
638
120
        let hasher = FxBuildHasher;
639
120
        let mut already_checked =
640
120
            FxHashSet::with_capacity_and_hasher(board_size_usize * board_size_usize, hasher);
641

            
642
120
        already_checked.insert(exit);
643

            
644
120
        let mut pre_stack = Vec::with_capacity(board_size_usize * board_size_usize);
645
120
        let up = expand_flood_fill(exit.up(), &mut already_checked, &mut pre_stack);
646
120
        let left = expand_flood_fill(exit.left(), &mut already_checked, &mut pre_stack);
647
120
        let down = expand_flood_fill(exit.down(), &mut already_checked, &mut pre_stack);
648
120
        let right = expand_flood_fill(exit.right(), &mut already_checked, &mut pre_stack);
649

            
650
120
        if up
651
60
            && left
652
30
            && self.get(&pre_stack[0]) == Space::Attacker
653
18
            && self.get(&pre_stack[1]) == Space::Attacker
654
18
            && self.get(&pre_stack[0].up().unwrap()) == Space::Attacker
655
12
            && self.get(&pre_stack[1].left().unwrap()) == Space::Attacker
656
        {
657
6
            return true;
658
114
        }
659

            
660
114
        if up
661
54
            && right
662
30
            && self.get(&pre_stack[0]) == Space::Attacker
663
18
            && self.get(&pre_stack[1]) == Space::Attacker
664
18
            && self.get(&pre_stack[0].up().unwrap()) == Space::Attacker
665
12
            && self.get(&pre_stack[1].right().unwrap()) == Space::Attacker
666
        {
667
6
            return true;
668
108
        }
669

            
670
108
        if left
671
54
            && down
672
30
            && self.get(&pre_stack[0]) == Space::Attacker
673
18
            && self.get(&pre_stack[1]) == Space::Attacker
674
18
            && self.get(&pre_stack[0].left().unwrap()) == Space::Attacker
675
12
            && self.get(&pre_stack[1].down().unwrap()) == Space::Attacker
676
        {
677
6
            return true;
678
102
        }
679

            
680
102
        if down
681
54
            && right
682
30
            && self.get(&pre_stack[0]) == Space::Attacker
683
18
            && self.get(&pre_stack[1]) == Space::Attacker
684
18
            && self.get(&pre_stack[0].down().unwrap()) == Space::Attacker
685
12
            && self.get(&pre_stack[1].right().unwrap()) == Space::Attacker
686
        {
687
6
            return true;
688
96
        }
689

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

            
701
1704
        while !stack.is_empty() {
702
1674
            if let Some(vertex) = stack.pop() {
703
1674
                let space = self.get(&vertex);
704
1674
                if space == Space::Empty {
705
1338
                    let _ = expand_flood_fill(vertex.up(), &mut already_checked, &mut stack);
706
1338
                    let _ = expand_flood_fill(vertex.left(), &mut already_checked, &mut stack);
707
1338
                    let _ = expand_flood_fill(vertex.down(), &mut already_checked, &mut stack);
708
1338
                    let _ = expand_flood_fill(vertex.right(), &mut already_checked, &mut stack);
709
1338
                } else if Into::<Role>::into(space) == Role::Defender {
710
66
                    return false;
711
270
                }
712
            }
713
        }
714

            
715
30
        true
716
120
    }
717

            
718
    #[must_use]
719
30
    pub fn closed_off_exits(&self) -> u8 {
720
30
        let mut closed_off_exits = 0;
721

            
722
120
        for exit in self.exit_squares() {
723
120
            if self.closed_off_exit(exit) {
724
54
                closed_off_exits += 1;
725
66
            }
726
        }
727

            
728
30
        closed_off_exits
729
30
    }
730

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

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

            
756
        if let (Some(role), Some(from), Some(to)) = (role, from, to) {
757
            Some(Plae::Play(Play { role, from, to }))
758
        } else {
759
            None
760
        }
761
    }
762

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

            
771
    #[must_use]
772
12129011
    pub fn find_the_king(&self) -> Option<Vertex> {
773
12129011
        let size = self.size();
774
12129011
        let board_size_usize: usize = size.into();
775

            
776
72211564
        for y in 0..board_size_usize {
777
734031594
            for x in 0..board_size_usize {
778
734031594
                let v = Vertex { size, x, y };
779
734031594
                if self.get(&v) == Space::King {
780
12127218
                    return Some(v);
781
721904376
                }
782
            }
783
        }
784

            
785
1793
        None
786
12129011
    }
787

            
788
    #[must_use]
789
3008985
    pub fn captured_the_king(&self) -> bool {
790
182135100
        for space in &self.spaces {
791
182135100
            if *space == Space::King {
792
3007480
                return false;
793
179127620
            }
794
        }
795

            
796
1505
        true
797
3008985
    }
798

            
799
3031615
    fn capture_the_king(
800
3031615
        &mut self,
801
3031615
        role_from: Role,
802
3031615
        play_to: &Vertex,
803
3031615
        captures: &mut Vec<Vertex>,
804
3031615
    ) -> bool {
805
3031615
        if let Some(kings_vertex) = self.find_the_king()
806
3031519
            && role_from == Role::Attacker
807
        {
808
1533445
            let mut attacker_moved = false;
809

            
810
1533445
            let (move_to_capture, surrounded) =
811
1533445
                self.capture_the_king_space(play_to, kings_vertex.up());
812

            
813
1533445
            if !surrounded {
814
1275020
                return false;
815
258425
            }
816
258425
            if move_to_capture {
817
45931
                attacker_moved = true;
818
212494
            }
819

            
820
258425
            let (move_to_capture, surrounded) =
821
258425
                self.capture_the_king_space(play_to, kings_vertex.left());
822

            
823
258425
            if !surrounded {
824
198724
                return false;
825
59701
            }
826
59701
            if move_to_capture {
827
10795
                attacker_moved = true;
828
48906
            }
829

            
830
59701
            let (move_to_capture, surrounded) =
831
59701
                self.capture_the_king_space(play_to, kings_vertex.down());
832

            
833
59701
            if !surrounded {
834
48881
                return false;
835
10820
            }
836
10820
            if move_to_capture {
837
1976
                attacker_moved = true;
838
8844
            }
839

            
840
10820
            let (move_to_capture, surrounded) =
841
10820
                self.capture_the_king_space(play_to, kings_vertex.right());
842

            
843
10820
            if !surrounded {
844
9144
                return false;
845
1676
            }
846
1676
            if move_to_capture {
847
176
                attacker_moved = true;
848
1500
            }
849

            
850
1676
            if attacker_moved {
851
1536
                self.set(&kings_vertex, Space::Empty);
852
1536
                captures.push(kings_vertex);
853
1536
                return true;
854
140
            }
855
1498170
        }
856

            
857
1498310
        false
858
3031615
    }
859

            
860
    #[must_use]
861
14874
    pub fn capture_the_king_one_move(&self) -> Option<Vertex> {
862
14874
        let mut spaces_left = 4;
863
14874
        let mut capture = None;
864

            
865
14874
        if let Some(kings_vertex) = self.find_the_king() {
866
14874
            if let Some(vertex) = kings_vertex.up() {
867
14845
                if vertex.on_throne() || self.get(&vertex) == Space::Attacker {
868
1080
                    spaces_left -= 1;
869
13765
                } else {
870
13765
                    capture = Some(vertex);
871
13765
                }
872
29
            }
873

            
874
14874
            if let Some(vertex) = kings_vertex.left() {
875
14842
                if vertex.on_throne() || self.get(&vertex) == Space::Attacker {
876
1084
                    spaces_left -= 1;
877
13758
                } else {
878
13758
                    capture = Some(vertex);
879
13758
                }
880
32
            }
881

            
882
14874
            if let Some(vertex) = kings_vertex.down() {
883
14855
                if vertex.on_throne() || self.get(&vertex) == Space::Attacker {
884
1452
                    spaces_left -= 1;
885
13403
                } else {
886
13403
                    capture = Some(vertex);
887
13403
                }
888
19
            }
889

            
890
14874
            if let Some(vertex) = kings_vertex.right() {
891
14862
                if vertex.on_throne() || self.get(&vertex) == Space::Attacker {
892
949
                    spaces_left -= 1;
893
13913
                } else {
894
13913
                    capture = Some(vertex);
895
13913
                }
896
12
            }
897
        }
898

            
899
14874
        if spaces_left == 1 { capture } else { None }
900
14874
    }
901

            
902
    #[inline]
903
1862391
    fn capture_the_king_space(&self, play_to: &Vertex, direction: Option<Vertex>) -> (bool, bool) {
904
1862391
        if let Some(surround_king) = direction {
905
1789517
            let move_to_capture = *play_to == surround_king;
906

            
907
1789517
            let surrounded = move_to_capture
908
1730639
                || surround_king.on_throne()
909
1719159
                || self.get(&surround_king) == Space::Attacker;
910

            
911
1789517
            (move_to_capture, surrounded)
912
        } else {
913
72874
            (false, false)
914
        }
915
1862391
    }
916

            
917
3030079
    fn exit_forts(&self) -> bool {
918
3030079
        match self.find_the_king() {
919
3029983
            Some(kings_vertex) => {
920
3029983
                kings_vertex.touches_wall()
921
354248
                    && self.able_to_move(&kings_vertex)
922
312726
                    && self.flood_fill_defender_wins(&kings_vertex)
923
            }
924
96
            None => false,
925
        }
926
3030079
    }
927

            
928
    #[inline]
929
3028567
    fn flood_fill_attacker_wins(&self) -> bool {
930
3028567
        let size = self.size();
931
3028567
        let board_size_usize: usize = size.into();
932

            
933
3028567
        match self.find_the_king() {
934
3028471
            Some(kings_vertex) => {
935
3028471
                let hasher = FxBuildHasher;
936
3028471
                let mut already_checked = FxHashSet::with_capacity_and_hasher(
937
3028471
                    board_size_usize * board_size_usize,
938
3028471
                    hasher,
939
                );
940

            
941
3028471
                already_checked.insert(kings_vertex);
942
3028471
                let mut stack = Vec::with_capacity(board_size_usize * board_size_usize);
943
3028471
                stack.push(kings_vertex);
944

            
945
28787038
                while !stack.is_empty() {
946
28785906
                    if let Some(vertex) = stack.pop() {
947
28785906
                        let space = self.get(&vertex);
948
28785906
                        if space == Space::Empty || Role::from(space) == Role::Defender {
949
22423929
                            if !expand_flood_fill(vertex.up(), &mut already_checked, &mut stack) {
950
106887
                                return false;
951
22317042
                            }
952
22317042
                            if !expand_flood_fill(vertex.left(), &mut already_checked, &mut stack) {
953
121781
                                return false;
954
22195261
                            }
955
22195261
                            if !expand_flood_fill(vertex.down(), &mut already_checked, &mut stack) {
956
308212
                                return false;
957
21887049
                            }
958
21887049
                            if !expand_flood_fill(vertex.right(), &mut already_checked, &mut stack)
959
                            {
960
2490459
                                return false;
961
19396590
                            }
962
6361977
                        }
963
                    }
964
                }
965

            
966
10263
                for y in 0..board_size_usize {
967
112011
                    for x in 0..board_size_usize {
968
112011
                        let vertex = Vertex { size, x, y };
969
112011
                        if Role::from(self.get(&vertex)) == Role::Defender
970
5362
                            && !already_checked.contains(&vertex)
971
                        {
972
321
                            return false;
973
111690
                        }
974
                    }
975
                }
976

            
977
811
                true
978
            }
979
96
            None => false,
980
        }
981
3028567
    }
982

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

            
989
312738
        let mut attacker_has_enough_pieces = false;
990
312738
        let mut count = 0;
991
370118
        'outer: for y in 0..board_size_usize {
992
2508575
            for x in 0..board_size_usize {
993
2508575
                let vertex = Vertex { size, x, y };
994
2508575
                if Role::from(self.get(&vertex)) == Role::Attacker {
995
625458
                    count += 1;
996
1883117
                }
997

            
998
2508575
                if count > 1 {
999
312720
                    attacker_has_enough_pieces = true;
312720
                    break 'outer;
2195855
                }
            }
        }
312738
        let mut already_checked = FxHashSet::default();
312738
        let mut stack = vec![];
312738
        if let Some(vertex) = vertex.up() {
238838
            stack.push((vertex, Direction::LeftRight));
238838
        }
312738
        if let Some(vertex) = vertex.left() {
231482
            stack.push((vertex, Direction::UpDown));
231482
        }
312738
        if let Some(vertex) = vertex.down() {
232254
            stack.push((vertex, Direction::LeftRight));
232254
        }
312738
        if let Some(vertex) = vertex.right() {
235640
            stack.push((vertex, Direction::UpDown));
235640
        }
1214497
        while !stack.is_empty() {
1212979
            if let Some((vertex, direction)) = stack.pop() {
1212979
                let space = self.get(&vertex);
1212979
                if space == Space::Empty {
782540
                    if let Some(vertex) = vertex.up()
725635
                        && !already_checked.contains(&vertex)
616987
                    {
616987
                        stack.push((vertex, Direction::LeftRight));
616987
                        already_checked.insert(vertex);
616987
                    }
782540
                    if let Some(vertex) = vertex.left()
779124
                        && !already_checked.contains(&vertex)
412582
                    {
412582
                        stack.push((vertex, Direction::UpDown));
412582
                        already_checked.insert(vertex);
412582
                    }
782540
                    if let Some(vertex) = vertex.down()
678560
                        && !already_checked.contains(&vertex)
634187
                    {
634187
                        stack.push((vertex, Direction::LeftRight));
634187
                        already_checked.insert(vertex);
634468
                    }
782540
                    if let Some(vertex) = vertex.right()
633242
                        && !already_checked.contains(&vertex)
569716
                    {
569716
                        stack.push((vertex, Direction::UpDown));
569716
                        already_checked.insert(vertex);
569716
                    }
430439
                } else if Role::from(space) == Role::Attacker {
282985
                    return false;
147454
                } else if direction == Direction::UpDown {
103946
                    let mut vertex_1 = false;
103946
                    let mut vertex_2 = false;
103946
                    if let Some(vertex) = vertex.up() {
85464
                        if Role::from(self.get(&vertex)) == Role::Defender {
22884
                            vertex_1 = true;
62679
                        }
18482
                    } else {
18482
                        vertex_1 = true;
18482
                    }
103946
                    if let Some(vertex) = vertex.down() {
73475
                        if Role::from(self.get(&vertex)) == Role::Defender {
22683
                            vertex_2 = true;
50858
                        }
30471
                    } else {
30471
                        vertex_2 = true;
30471
                    }
103946
                    if !vertex_1 && !vertex_2 && attacker_has_enough_pieces {
22283
                        return false;
81663
                    }
                } else {
43508
                    let mut vertex_1 = false;
43508
                    let mut vertex_2 = false;
43508
                    if let Some(vertex) = vertex.right() {
22271
                        if Role::from(self.get(&vertex)) == Role::Defender {
11949
                            vertex_1 = true;
11949
                        }
21237
                    } else {
21237
                        vertex_1 = true;
21237
                    }
43508
                    if let Some(vertex) = vertex.left() {
41781
                        if Role::from(self.get(&vertex)) == Role::Defender {
11967
                            vertex_2 = true;
29852
                        }
1727
                    } else {
1727
                        vertex_2 = true;
1727
                    }
43508
                    if !vertex_1 && !vertex_2 && attacker_has_enough_pieces {
5952
                        return false;
37556
                    }
                }
            }
        }
1518
        true
312738
    }
    #[must_use]
1166281799
    pub fn get(&self, vertex: &Vertex) -> Space {
1166281799
        let board_size: usize = self.size().into();
1166281799
        self.spaces[vertex.y * board_size + vertex.x]
1166281799
    }
    #[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]
1235332971
    pub fn size(&self) -> BoardSize {
1235332971
        let len = self.spaces.len();
1235332971
        if len == 11 * 11 {
1235327331
            BoardSize::_11
5640
        } else if len == 13 * 13 {
5640
            BoardSize::_13
        } else {
            unreachable!()
        }
1235332971
    }
    /// # Errors
    ///
    /// If the move is illegal.
    #[allow(
        clippy::cast_possible_truncation,
        clippy::cast_possible_wrap,
        clippy::cast_sign_loss
    )]
25130826
    pub fn legal_move(
25130826
        &self,
25130826
        play: &Play,
25130826
        status: &Status,
25130826
        turn: &Role,
25130826
        previous_boards: &PreviousBoards,
25130826
    ) -> Result<Board, InvalidMove> {
25130826
        let size = self.size();
25130826
        if *status != Status::Ongoing {
            return Err(InvalidMove::GameOver);
25130826
        }
25130826
        let space_from = self.get(&play.from);
25130826
        let role_from = Role::from(space_from);
25130826
        if role_from == Role::Roleless {
            return Err(InvalidMove::Role);
25130826
        } else if *turn != role_from {
            return Err(InvalidMove::Turn);
25130826
        }
25130826
        let x_diff = play.from.x as i32 - play.to.x as i32;
25130826
        let y_diff = play.from.y as i32 - play.to.y as i32;
25130826
        if x_diff != 0 && y_diff != 0 {
54
            return Err(InvalidMove::StraightLine);
25130772
        }
25130772
        if x_diff == 0 && y_diff == 0 {
1465619
            return Err(InvalidMove::Location);
23665153
        }
23665153
        if x_diff != 0 {
13441313
            let x_diff_sign = x_diff.signum();
29401449
            for x_diff in 1..=x_diff.abs() {
29401449
                let vertex = Vertex {
29401449
                    size,
29401449
                    x: (play.from.x as i32 - (x_diff * x_diff_sign)) as usize,
29401449
                    y: play.from.y,
29401449
                };
29401449
                let space = self.get(&vertex);
29401449
                if space != Space::Empty {
6818277
                    return Err(InvalidMove::Empty);
22583172
                }
            }
        } else {
10223840
            let y_diff_sign = y_diff.signum();
20834788
            for y_diff in 1..=y_diff.abs() {
20834788
                let vertex = Vertex {
20834788
                    size,
20834788
                    x: play.from.x,
20834788
                    y: (play.from.y as i32 - (y_diff * y_diff_sign)) as usize,
20834788
                };
20834788
                let space = self.get(&vertex);
20834788
                if space != Space::Empty {
5760268
                    return Err(InvalidMove::Empty);
15074520
                }
            }
        }
11086608
        if space_from != Space::King && play.to.on_restricted_square() {
1663492
            return Err(InvalidMove::Restricted);
9423116
        }
9423116
        let mut board = self.clone();
9423116
        board.set(&play.from, Space::Empty);
9423116
        board.set(&play.to, space_from);
9423116
        if turn == &Role::Defender && previous_boards.0.contains(&board) {
3328
            return Err(InvalidMove::RepeatMove);
9419788
        }
9419788
        Ok(board)
25130826
    }
    #[must_use]
3027756
    fn no_attacker_pieces_left(&self) -> bool {
3027756
        let size = self.size();
3027756
        let board_size_usize: usize = size.into();
3096754
        for y in 0..board_size_usize {
13556248
            for x in 0..board_size_usize {
13556248
                let v = Vertex { size, x, y };
13556248
                if Role::from(self.get(&v)) == Role::Attacker {
3027684
                    return false;
10528564
                }
            }
        }
72
        true
3027756
    }
    /// # Errors
    ///
    /// If the move is illegal.
3041186
    pub fn play(
3041186
        &mut self,
3041186
        play: &Plae,
3041186
        status: &Status,
3041186
        turn: &Role,
3041186
        previous_boards: &mut PreviousBoards,
3041186
    ) -> anyhow::Result<(Vec<Vertex>, Status)> {
3041186
        let (board, captures, status) = self.play_internal(play, status, turn, previous_boards)?;
3039818
        previous_boards.0.insert(board.clone());
3039818
        *self = board;
3039818
        Ok((captures, status))
3041186
    }
    /// # Errors
    ///
    /// If the move is illegal.
3041186
    pub fn play_internal(
3041186
        &self,
3041186
        play: &Plae,
3041186
        status: &Status,
3041186
        turn: &Role,
3041186
        previous_boards: &PreviousBoards,
3041186
    ) -> anyhow::Result<(Board, Vec<Vertex>, Status)> {
3041186
        if *status != Status::Ongoing {
            return Err(anyhow::Error::msg(
                "play: the game has to be ongoing to play",
            ));
3041186
        }
3041186
        let play = match play {
            Plae::AttackerResigns => return Ok((self.clone(), Vec::new(), Status::DefenderWins)),
            Plae::DefenderResigns => return Ok((self.clone(), Vec::new(), Status::AttackerWins)),
3041186
            Plae::Play(play) => play,
        };
3041186
        let mut board = self.legal_move(play, status, turn, previous_boards)?;
3039818
        let space_from = self.get(&play.from);
3039818
        let role_from = Role::from(space_from);
3039818
        let mut captures = Vec::new();
3039818
        board.captures(&play.to, role_from, &mut captures);
3039818
        board.captures_shield_wall(role_from, &play.to, &mut captures);
3039818
        if play.to.on_exit_square() {
8203
            return Ok((board, captures, Status::DefenderWins));
3031615
        }
3031615
        if board.capture_the_king(role_from, &play.to, &mut captures) {
1536
            return Ok((board, captures, Status::AttackerWins));
3030079
        }
3030079
        if board.exit_forts() {
1512
            return Ok((board, captures, Status::DefenderWins));
3028567
        }
3028567
        if board.flood_fill_attacker_wins() {
811
            return Ok((board, captures, Status::AttackerWins));
3027756
        }
3027756
        if board.no_attacker_pieces_left() {
72
            return Ok((board, captures, Status::DefenderWins));
3027684
        }
3027684
        Ok((board, captures, Status::Ongoing))
3041186
    }
19308506
    fn set(&mut self, vertex: &Vertex, space: Space) {
19308506
        let board_size: usize = self.size().into();
19308506
        self.spaces[vertex.y * board_size + vertex.x] = space;
19308506
    }
    #[must_use]
460821
    fn set_if_not_king(&mut self, vertex: &Vertex, space: Space) -> bool {
460821
        if self.get(vertex) == Space::King {
83
            false
        } else {
460738
            self.set(vertex, space);
460738
            true
        }
460821
    }
    #[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 {
1266922074
    fn from(size: BoardSize) -> Self {
1266922074
        match size {
1266916326
            BoardSize::_11 => 11,
5748
            BoardSize::_13 => 13,
        }
1266922074
    }
}
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;
6582462
    fn try_from(value: usize) -> Result<Self, Self::Error> {
6582462
        match value {
6582438
            11 => Ok(BoardSize::_11),
24
            13 => Ok(BoardSize::_13),
            _ => Err(anyhow::Error::msg(format!(
                "an invalid board size was passed: {value}"
            ))),
        }
6582462
    }
}
#[must_use]
#[allow(clippy::missing_panics_doc)]
#[allow(clippy::unwrap_used)]
123360
fn board_11x11() -> Board {
123360
    let spaces: Vec<Space> = STARTING_POSITION_11X11
123360
        .iter()
14926560
        .flat_map(|space| space.chars().map(|ch| ch.try_into().unwrap()))
123360
        .collect();
123360
    Board {
123360
        spaces,
123360
        display_ascii: false,
123360
    }
123360
}
#[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]
88829881
fn expand_flood_fill(
88829881
    vertex: Option<Vertex>,
88829881
    already_checked: &mut FxHashSet<Vertex>,
88829881
    stack: &mut Vec<Vertex>,
88829881
) -> bool {
88829881
    if let Some(vertex) = vertex {
85801432
        if !already_checked.contains(&vertex) {
58388826
            stack.push(vertex);
58388826
            already_checked.insert(vertex);
58388826
        }
85801432
        true
    } else {
3028449
        false
    }
88829881
}