1use std::{collections::HashSet, f32::consts::PI, hash::Hash};
2
3use crate::{emulator::TravelKind, turn::Score};
4
5struct IterableDA {
12 curr: f32,
13 step: f32,
14 inclusive_upper_bound: f32,
15}
16
17impl Iterator for IterableDA {
18 type Item = f32;
19
20 fn next(&mut self) -> Option<Self::Item> {
21 if self.curr > self.inclusive_upper_bound {
22 return None;
23 }
24 let ans = self.curr;
25 self.curr += self.step;
26 Some(ans)
27 }
28}
29
30#[derive(Debug, Clone, Copy)]
32struct DistancesAngle {
33 start: f32,
34 step: f32,
35 inclusive_upper_bound: f32,
36 angle: f32,
41}
42
43impl IntoIterator for DistancesAngle {
44 type Item = f32;
45
46 type IntoIter = IterableDA;
47
48 fn into_iter(self) -> Self::IntoIter {
49 IterableDA {
50 curr: self.start,
51 step: self.step,
52 inclusive_upper_bound: self.inclusive_upper_bound,
53 }
54 }
55}
56
57impl DistancesAngle {
58 const fn singleton(distance: f32, angle: f32) -> Self {
59 Self {
60 start: distance,
61 step: 1.,
62 inclusive_upper_bound: distance,
63 angle,
64 }
65 }
66
67 const fn repeated(start: f32, step: f32, n: i32, angle: f32) -> Self {
68 Self {
69 start,
70 step,
71 inclusive_upper_bound: start + step * (n - 1) as f32,
72 angle,
73 }
74 }
75
76 const fn range(start: f32, step: f32, inclusive_upper_bound: f32, angle: f32) -> Self {
77 Self {
78 start,
79 step,
80 inclusive_upper_bound,
81 angle,
82 }
83 }
84}
85
86impl DistancesAngle {
87 fn get_offsets(&self, angle: f32) -> impl Iterator<Item = (f32, f32)> {
89 self.clone()
90 .into_iter()
91 .map(move |d| self.get_point(d, self.angle, angle))
92 }
93
94 fn get_point(&self, distance: f32, base_angle: f32, offset_angle: f32) -> (f32, f32) {
98 let angle = base_angle - offset_angle;
99 crate::floating_drift::floating_drift_adjust!(
100 distance * f32::cos(angle),
101 distance * f32::sin(angle),
102 )
103 }
104}
105
106#[cfg(test)]
107mod da_tests {
108 use super::DistancesAngle;
109
110 #[test]
111 fn rep() {
112 let n = 5;
113 let da = DistancesAngle::repeated(1., 2., n, 5.);
114 assert_eq!(n as usize, da.into_iter().collect::<Vec<f32>>().len());
115 }
116
117 #[test]
118 fn rep_45deg() {
119 let (start, step, n, angle) = (0., f32::sqrt(2.), 4, 45.0_f32.to_radians());
120 let da = DistancesAngle::repeated(start, step, n, angle);
121 let offset_angle = 0.;
122 assert_eq![
123 vec![(0., 0.), (1., 1.), (2., 2.), (3., 3.)],
124 da.get_offsets(offset_angle).collect::<Vec<(f32, f32)>>()
125 ];
126 }
127}
128
129#[derive(Debug, Clone, Copy, PartialEq)]
130pub enum Side {
131 Black,
132 White,
133}
134
135impl Side {
136 pub fn to_file_desc(&self) -> &str {
137 match self {
138 Side::Black => "B",
139 Side::White => "W",
140 }
141 }
142
143 pub fn toggle(&self) -> Self {
144 match self {
145 Side::Black => Side::White,
146 Side::White => Side::Black,
147 }
148 }
149}
150
151#[derive(Debug, Clone, Copy, PartialEq)]
152pub enum PieceKind {
153 Pawn,
154 Rook,
155 Knight,
156 Bishop,
157 Queen,
158 King,
159}
160
161impl PieceKind {
162 pub fn to_file_desc(&self) -> &str {
163 match self {
164 PieceKind::Pawn => "pawn",
165 PieceKind::Rook => "rook",
166 PieceKind::Knight => "knight",
167 PieceKind::Bishop => "bishop",
168 PieceKind::Queen => "queen",
169 PieceKind::King => "king",
170 }
171 }
172
173 pub fn value(&self) -> Score {
174 match self {
175 PieceKind::Pawn => 1.0,
176 PieceKind::Rook => 5.0,
177 PieceKind::Knight => 3.0,
178 PieceKind::Bishop => 3.0,
179 PieceKind::Queen => 9.0,
180 PieceKind::King => 1000.0,
181 }
182 }
183
184 pub fn can_jump(&self) -> bool {
185 match self {
186 PieceKind::Pawn => true,
187 PieceKind::King => true,
188 PieceKind::Knight => true,
189 PieceKind::Rook => false,
190 PieceKind::Bishop => false,
191 PieceKind::Queen => false,
192 }
193 }
194
195 pub fn can_promote(&self) -> bool {
196 *self == PieceKind::Pawn
197 }
198
199 fn add_level_das(v: &mut Vec<DistancesAngle>) {
201 for i in 0..4 {
202 v.push(DistancesAngle::range(
203 1.,
204 1.,
205 f32::INFINITY,
206 i as f32 * PI / 2.,
207 ))
208 }
209 }
210
211 const KNIGHT_DISTANCE: f32 = 2.23606797749979;
213
214 const KNIGHT_ANGLES: [f32; 8] = [
215 0.4636476090008061,
216 -0.4636476090008061,
217 -1.1071487177940904,
218 -2.0344439357957027,
219 -2.677945044588987,
220 2.677945044588987,
221 2.0344439357957027,
222 1.1071487177940904,
223 ];
224
225 fn add_diag_das(v: &mut Vec<DistancesAngle>) {
227 for i in 0..4 {
228 v.push(DistancesAngle::range(
229 f32::sqrt(2.),
230 f32::sqrt(2.),
231 f32::INFINITY,
232 (i as f32 * PI / 2.) + (PI / 4.),
233 ))
234 }
235 }
236
237 fn get_capture_das(&self) -> Vec<DistancesAngle> {
238 let mut ans = vec![];
239 match self {
240 PieceKind::Pawn => {
241 ans.push(DistancesAngle::singleton(f32::sqrt(2.), PI / 4.));
242 ans.push(DistancesAngle::singleton(f32::sqrt(2.), -PI / 4.));
243 }
244 PieceKind::King => {
245 for i in 0..8 {
246 ans.push(DistancesAngle::singleton(
247 if i % 2 == 0 { 1. } else { f32::sqrt(2.) },
248 i as f32 * PI / 4.,
249 ))
250 }
251 }
252 PieceKind::Knight => {
253 for rad in PieceKind::KNIGHT_ANGLES {
254 ans.push(DistancesAngle::singleton(PieceKind::KNIGHT_DISTANCE, rad));
255 }
256 }
257 PieceKind::Rook => PieceKind::add_level_das(&mut ans),
258 PieceKind::Bishop => PieceKind::add_diag_das(&mut ans),
259 PieceKind::Queen => {
260 PieceKind::add_level_das(&mut ans);
261 PieceKind::add_diag_das(&mut ans);
262 }
263 };
264 ans
265 }
266
267 fn get_move_das(&self) -> Vec<DistancesAngle> {
268 let mut ans = vec![];
269 match self {
270 PieceKind::Pawn => ans.push(DistancesAngle::repeated(1., 1., 2, 0.)),
271 PieceKind::King => {
272 for i in 0..8 {
273 ans.push(DistancesAngle::singleton(
274 if i % 2 == 0 { 1. } else { f32::sqrt(2.) },
275 i as f32 * PI / 4.,
276 ))
277 }
278 }
279 PieceKind::Knight => {
280 for rad in PieceKind::KNIGHT_ANGLES {
281 ans.push(DistancesAngle::singleton(PieceKind::KNIGHT_DISTANCE, rad));
282 }
283 }
284 PieceKind::Rook => PieceKind::add_level_das(&mut ans),
285 PieceKind::Bishop => PieceKind::add_diag_das(&mut ans),
286 PieceKind::Queen => {
287 PieceKind::add_level_das(&mut ans);
288 PieceKind::add_diag_das(&mut ans);
289 }
290 };
291 ans
292 }
293}
294
295pub const PIECE_RADIUS: f32 = 17.0 / 50.0;
300
301#[derive(Debug, Clone)]
307struct CorePieceData {
308 center: (f32, f32),
309 angle: f32,
310 side: Side,
311 kind: PieceKind,
312}
313
314impl Hash for CorePieceData {
315 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
316 self.center.0.to_be_bytes().hash(state);
317 self.center.1.to_be_bytes().hash(state);
318 }
319}
320
321impl PartialEq for CorePieceData {
322 fn eq(&self, other: &Self) -> bool {
323 self.center == other.center
324 && self.angle == other.angle
325 && self.side == other.side
326 && self.kind == other.kind
327 }
328}
329impl Eq for CorePieceData {}
330
331#[derive(Debug, Clone)]
340struct SecondaryPieceData {
341 capture_das: Vec<DistancesAngle>,
343 move_das: Vec<DistancesAngle>,
345}
346
347impl From<&CorePieceData> for SecondaryPieceData {
348 fn from(core: &CorePieceData) -> Self {
349 Self {
350 capture_das: core.kind.get_capture_das(),
351 move_das: core.kind.get_move_das(),
352 }
353 }
354}
355
356#[derive(Clone)]
357struct TertiaryPieceData {
358 capture_points: Vec<(f32, f32)>,
360 move_points: Vec<(f32, f32)>,
362}
363
364impl From<(&CorePieceData, &SecondaryPieceData)> for TertiaryPieceData {
365 fn from((core, sec): (&CorePieceData, &SecondaryPieceData)) -> Self {
366 let mut capture_points = vec![];
367 let mut move_points = vec![];
368 Piece::extend_with_drawable_points(core, &mut capture_points, sec.capture_das.iter());
369 Piece::extend_with_drawable_points(core, &mut move_points, sec.move_das.iter());
370 Self {
371 capture_points,
372 move_points,
373 }
374 }
375}
376
377#[derive(Clone)]
378pub struct Piece {
379 core: CorePieceData,
380 secondary: Option<SecondaryPieceData>,
381 tertiary: Option<TertiaryPieceData>,
382}
383
384impl std::fmt::Display for Piece {
385 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
386 write!(
387 f,
388 "Piece(x={}, y={}, side={:?}), kind={:?}",
389 self.core.center.0, self.core.center.1, self.core.side, self.core.kind
390 )
391 }
392}
393
394impl Hash for Piece {
395 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
396 self.core.hash(state);
397 }
398}
399
400impl PartialEq for Piece {
401 fn eq(&self, other: &Self) -> bool {
402 self.core == other.core
403 }
404}
405impl Eq for Piece {}
406
407impl Piece {
409 pub fn new(center: (f32, f32), angle: f32, side: Side, kind: PieceKind) -> Self {
410 Self {
411 core: CorePieceData {
412 center,
413 angle,
414 side,
415 kind,
416 },
417 secondary: None,
418 tertiary: None,
419 }
420 }
421
422 pub fn from_tile(tile: (u8, u8), angle: f32, side: Side, kind: PieceKind) -> Self {
424 let (x, y) = tile;
425 Self {
426 core: CorePieceData {
427 center: (x as f32 + 0.5, y as f32 + 0.5),
428 angle,
429 side,
430 kind,
431 },
432 secondary: None,
433 tertiary: None,
434 }
435 }
436}
437
438impl Piece {
440 pub fn center(&self) -> (f32, f32) {
441 self.core.center
442 }
443
444 pub fn x(&self) -> f32 {
445 self.core.center.0
446 }
447
448 pub fn set_x(&mut self, x: f32) {
449 self.core.center.0 = x;
450 }
451
452 pub fn y(&self) -> f32 {
453 self.core.center.1
454 }
455
456 pub fn set_y(&mut self, y: f32) {
457 self.core.center.1 = y;
458 }
459
460 pub fn set_pos(&mut self, x: f32, y: f32) {
461 self.core.center.0 = x;
462 self.core.center.1 = y;
463 }
464
465 pub fn angle(&self) -> f32 {
466 self.core.angle
467 }
468
469 pub fn set_angle(&mut self, angle: f32) {
470 self.core.angle = angle;
471 }
472
473 pub fn side(&self) -> Side {
474 self.core.side
475 }
476
477 pub fn kind(&self) -> PieceKind {
478 self.core.kind
479 }
480
481 pub fn set_kind(&mut self, kind: PieceKind) {
482 self.core.kind = kind;
483 }
484
485 pub fn needs_init(&self) -> bool {
486 self.secondary.is_none() || self.tertiary.is_none()
487 }
488}
489
490impl Piece {
492 pub fn forward_distance(&self) -> f32 {
494 match self.side() {
495 Side::Black => self.y(),
496 Side::White => 8.0 - self.y(),
497 }
498 }
499
500 pub fn collidepoint_generic(x1: f32, y1: f32, x2: f32, y2: f32) -> bool {
501 (x1 - x2).powi(2) + (y1 - y2).powi(2) < PIECE_RADIUS.powi(2)
502 }
503
504 pub fn collidepoint(&self, x: f32, y: f32) -> bool {
505 Piece::collidepoint_generic(x, y, self.core.center.0, self.core.center.1)
506 }
507
508 pub fn collidepiece(&self, x: f32, y: f32) -> bool {
510 ((x - self.core.center.0).powi(2) + (y - self.core.center.1).powi(2))
511 < (PIECE_RADIUS * 2.).powi(2)
512 }
513
514 pub fn on_board(x: f32, y: f32) -> bool {
518 const BOARD_SIZE: f32 = 8.;
519 const MARGIN: f32 = PIECE_RADIUS;
520 !(x < 0. - MARGIN || x > BOARD_SIZE + MARGIN || y < 0. - MARGIN || y > BOARD_SIZE + MARGIN)
521 }
522
523 pub fn should_promote(kind: PieceKind, side: Side, y: f32) -> bool {
527 if !kind.can_promote() {
528 return false;
529 }
530
531 match side {
532 Side::Black => y + PIECE_RADIUS > 7.,
533 Side::White => y - PIECE_RADIUS < 1.,
534 }
535 }
536
537 pub fn capture_points_unchecked(&self) -> impl Iterator<Item = &(f32, f32)> {
538 let tertiary = self
539 .tertiary
540 .as_ref()
541 .expect("Invariant was that delayed is Some.");
542
543 tertiary.capture_points.iter()
544 }
545
546 pub fn move_points_unchecked(&self) -> impl Iterator<Item = &(f32, f32)> {
547 let tertiary = self
548 .tertiary
549 .as_ref()
550 .expect("Invariant was that delayed is Some.");
551
552 tertiary.move_points.iter()
553 }
554
555 pub fn init_auxiliary_data(&mut self) {
556 self.secondary = Some(SecondaryPieceData::from(&self.core));
557 self.tertiary = Some(TertiaryPieceData::from((
558 &self.core,
559 self.secondary.as_ref().expect("We just created this."),
560 )));
561 }
562
563 pub fn update_capmove_points_unchecked(&mut self) {
564 self.update_capture_points_unchecked();
565 self.update_move_points_unchecked();
566 }
567
568 pub fn update_capture_points_unchecked(&mut self) {
570 let capture_points: &mut Vec<(f32, f32)> =
571 &mut self.tertiary.as_mut().expect("Invariant.").capture_points;
572 let capture_das: &Vec<DistancesAngle> =
573 &self.secondary.as_ref().expect("Invariant.").capture_das;
574
575 capture_points.clear();
576 Piece::extend_with_drawable_points(&self.core, capture_points, capture_das.iter());
577 }
578
579 pub fn update_move_points_unchecked(&mut self) {
581 let move_points: &mut Vec<(f32, f32)> =
582 &mut self.tertiary.as_mut().expect("Invariant.").move_points;
583 let move_das: &Vec<DistancesAngle> = &self.secondary.as_ref().expect("Invariant.").move_das;
584
585 move_points.clear();
586 Piece::extend_with_drawable_points(&self.core, move_points, move_das.iter());
587 }
588
589 fn extend_with_drawable_points<'a>(
598 core: &CorePieceData,
599 points: &mut Vec<(f32, f32)>,
600 das: impl Iterator<Item = &'a DistancesAngle>,
601 ) {
602 for da in das {
603 for (x, y) in da.get_offsets(core.angle + PI / 2.) {
604 let point = (x + core.center.0, y + core.center.1);
605 if !Piece::on_board(point.0, point.1) {
606 break;
607 }
608 points.push(point);
609 }
610 }
611 }
612}
613
614fn scalar_comp(
616 start_x: f32,
617 start_y: f32,
618 point_x: f32,
619 point_y: f32,
620 dir_x: f32,
621 dir_y: f32,
622) -> f32 {
623 let u = (dir_x - start_x, dir_y - start_y);
625 let v = (point_x - start_x, point_y - start_y);
626
627 (u.0 * v.0 + u.1 * v.1) / f32::sqrt(u.0.powi(2) + u.1.powi(2))
628}
629
630fn max_hit_distance(start_x: f32, start_y: f32, end_x: f32, end_y: f32) -> f32 {
634 f32::sqrt((start_x - end_x).powi(2) + (start_y - end_y).powi(2)) + PIECE_RADIUS
635}
636
637fn point_to_line_dist(
639 start_x: f32,
640 start_y: f32,
641 end_x: f32,
642 end_y: f32,
643 point_x: f32,
644 point_y: f32,
645) -> f32 {
646 f32::abs((end_x - start_x) * (point_y - start_y) - (point_x - start_x) * (end_y - start_y))
647 / f32::sqrt((end_x - start_x).powi(2) + (end_y - start_y).powi(2))
648}
649
650#[derive(Clone)]
657pub struct Pieces {
658 inner: Vec<Piece>,
659}
660
661impl Pieces {
662 pub fn standard_board() -> Self {
664 let mut inner = vec![];
665
666 const ORDER: [PieceKind; 8] = [
667 PieceKind::Rook,
668 PieceKind::Knight,
669 PieceKind::Bishop,
670 PieceKind::Queen,
671 PieceKind::King,
672 PieceKind::Bishop,
673 PieceKind::Knight,
674 PieceKind::Rook,
675 ];
676
677 for i in 0..8 {
678 inner.push(Piece::from_tile((i, 1), -PI, Side::Black, PieceKind::Pawn));
679 inner.push(Piece::from_tile((i, 6), 0., Side::White, PieceKind::Pawn));
680 }
681
682 for (i, kind) in ORDER.iter().enumerate() {
683 inner.push(Piece::from_tile((i as u8, 0), -PI, Side::Black, *kind));
684 inner.push(Piece::from_tile((i as u8, 7), 0., Side::White, *kind));
685 }
686
687 Self { inner }
688 }
689
690 pub fn chess960_board(idx_ordering: impl FnOnce() -> [usize; 8]) -> Self {
694 let mut inner = vec![];
695
696 let pieces: [PieceKind; 8] = [
697 PieceKind::Rook,
698 PieceKind::Knight,
699 PieceKind::Bishop,
700 PieceKind::Queen,
701 PieceKind::King,
702 PieceKind::Bishop,
703 PieceKind::Knight,
704 PieceKind::Rook,
705 ];
706
707 let ordering = idx_ordering();
708 debug_assert!({
709 let mut ordering2 = ordering.to_vec();
710 ordering2.sort();
711 ordering2 == (0..8).collect::<Vec<_>>()
712 });
713 let order = ordering.map(|i| pieces[i]);
714
715 for i in 0..8 {
716 inner.push(Piece::from_tile((i, 1), -PI, Side::Black, PieceKind::Pawn));
717 inner.push(Piece::from_tile((i, 6), 0., Side::White, PieceKind::Pawn));
718 }
719
720 for (i, kind) in order.iter().enumerate() {
721 inner.push(Piece::from_tile((i as u8, 0), -PI, Side::Black, *kind));
722 inner.push(Piece::from_tile((i as u8, 7), 0., Side::White, *kind));
723 }
724
725 Self { inner }
726 }
727
728 pub fn get_piece(&self, x: f32, y: f32) -> Option<usize> {
730 self.inner.iter().position(|piece| piece.collidepoint(x, y))
731 }
732
733 pub fn get(&self, index: usize) -> Option<&Piece> {
734 self.inner.get(index)
735 }
736
737 pub fn get_mut(&mut self, index: usize) -> Option<&mut Piece> {
738 self.inner.get_mut(index)
739 }
740
741 pub fn inner_ref(&self) -> &[Piece] {
742 &self.inner
743 }
744
745 pub fn inner_mut(&mut self) -> &mut Vec<Piece> {
746 &mut self.inner
747 }
748
749 pub fn travel(&mut self, idx: usize, x: f32, y: f32) -> usize {
755 let orig_piece_center = self.inner[idx].center();
756 self.inner.retain(|piece| !piece.collidepiece(x, y));
757 let new_idx = self
758 .inner
759 .iter()
760 .position(|p| p.center() == orig_piece_center)
761 .expect("Should still exist.");
762
763 let piece = &mut self.inner[new_idx];
764 piece.set_x(x);
765 piece.set_y(y);
766 if Piece::should_promote(piece.kind(), piece.side(), y) {
767 piece.set_kind(PieceKind::Queen);
768 piece.init_auxiliary_data();
769 }
770
771 new_idx
772 }
773
774 pub fn travelable(&self, piece: &Piece, x: f32, y: f32, kind: TravelKind) -> bool {
775 let mut pieces_overlapping_endpoint = HashSet::new();
777
778 for other_piece in &self.inner {
780 if other_piece == piece {
781 debug_assert!(!piece.needs_init());
782 continue;
783 }
784
785 if other_piece.collidepiece(x, y) {
786 pieces_overlapping_endpoint.insert(other_piece);
787
788 if other_piece.side() == piece.side() {
789 return false;
790 }
791 }
792 }
793
794 if piece.core.kind.can_jump() {
795 match kind {
796 TravelKind::Capture => {
797 if !pieces_overlapping_endpoint.is_empty() {
798 return true;
799 }
800 }
801 TravelKind::Move => return pieces_overlapping_endpoint.is_empty(),
802 };
803 }
804
805 let mut in_the_way = 0;
806 for other_piece in &self.inner {
807 if other_piece == piece {
808 continue;
809 }
810
811 let comp = scalar_comp(piece.x(), piece.y(), other_piece.x(), other_piece.y(), x, y);
812 if 0. < comp && comp < max_hit_distance(piece.x(), piece.y(), x, y) {
813 if point_to_line_dist(piece.x(), piece.y(), x, y, other_piece.x(), other_piece.y())
815 < 2. * PIECE_RADIUS
816 {
817 if !pieces_overlapping_endpoint.contains(&other_piece) {
820 in_the_way += 1;
821 }
822 }
823 }
824 }
825
826 if in_the_way > 0 {
831 return false;
832 }
833
834 debug_assert!(
835 pieces_overlapping_endpoint
836 .iter()
837 .all(|other_piece| other_piece.side() != piece.side())
838 );
839 match kind {
840 TravelKind::Capture => !pieces_overlapping_endpoint.is_empty(),
841 TravelKind::Move => pieces_overlapping_endpoint.is_empty(),
842 }
843 }
844}