]> git.lizzy.rs Git - rs2048.git/blob - src/game.rs
Fix inner field area
[rs2048.git] / src / game.rs
1 pub use glam::i32::IVec2 as Pos;
2 use rand::seq::IteratorRandom;
3 use std::cell::RefCell;
4
5 trait Swap {
6     fn swap(self) -> Self;
7 }
8
9 impl Swap for Pos {
10     fn swap(self) -> Self {
11         Self::new(self.y, self.x)
12     }
13 }
14
15 pub struct Field(RefCell<u32>);
16
17 enum MergeResult {
18     Merged(u32),
19     Replaced,
20     Blocked,
21     Empty,
22 }
23
24 impl Field {
25     fn new() -> Self {
26         Self(RefCell::new(0))
27     }
28
29     pub fn value(&self) -> u32 {
30         *self.0.borrow()
31     }
32
33     fn merge(&self, other: &Self) -> MergeResult {
34         let mut s = self.0.borrow_mut();
35         let mut o = other.0.borrow_mut();
36
37         if *o == 0 {
38             return MergeResult::Empty;
39         }
40
41         if *s == 0 {
42             *s = *o;
43             *o = 0;
44
45             return MergeResult::Replaced;
46         }
47
48         if *s == *o {
49             *s += 1;
50             *o = 0;
51
52             return MergeResult::Merged(1 << *s);
53         }
54
55         MergeResult::Blocked
56     }
57 }
58
59 pub struct Board {
60     pub size: Pos,
61     fields: Vec<Vec<Field>>,
62 }
63
64 pub enum Dir {
65     Up,
66     Down,
67     Left,
68     Right,
69 }
70
71 impl Board {
72     pub fn new(size: Pos) -> Self {
73         Self {
74             size,
75             fields: (0..size.x)
76                 .map(|_| (0..size.y).map(|_| Field::new()).collect())
77                 .collect(),
78         }
79     }
80
81     pub fn step(&self, dir: Dir) -> Option<u32> {
82         let dir = match dir {
83             Dir::Up => -Pos::Y,
84             Dir::Down => Pos::Y,
85             Dir::Left => -Pos::X,
86             Dir::Right => Pos::X,
87         };
88
89         let step_row = dir.abs().swap();
90         let step_col = -dir;
91
92         let len_row = (step_row.abs() * self.size).max_element();
93         let len_col = (step_col.abs() * self.size).max_element();
94
95         let start = (dir + Pos::ONE) / 2 * (self.size - Pos::ONE);
96
97         let mut score = None;
98
99         for row in 0..len_row {
100             let start_row = start + row * step_row;
101
102             for col1 in 0..len_col - 1 {
103                 let field1 = self.get(start_row + col1 * step_col);
104
105                 for col2 in col1 + 1..len_col {
106                     let field2 = self.get(start_row + col2 * step_col);
107
108                     match field1.merge(field2) {
109                         MergeResult::Merged(sc) => {
110                             score = Some(score.unwrap_or(0) + sc);
111                             break;
112                         }
113                         MergeResult::Replaced => score = Some(score.unwrap_or(0)),
114                         MergeResult::Blocked => break,
115                         MergeResult::Empty => continue,
116                     }
117                 }
118             }
119         }
120
121         score
122     }
123
124     pub fn get(&self, pos: Pos) -> &Field {
125         self.fields
126             .get(pos.x as usize)
127             .unwrap()
128             .get(pos.y as usize)
129             .unwrap()
130     }
131
132     pub fn spawn<R>(&self, rng: &mut R)
133     where
134         R: rand::Rng + ?core::marker::Sized,
135     {
136         if let Some(field) = self
137             .fields
138             .iter()
139             .flat_map(|v| v.iter())
140             .filter(|f| f.value() == 0)
141             .choose(rng)
142         {
143             *field.0.borrow_mut() = 1;
144         }
145     }
146 }