]> git.lizzy.rs Git - rs2048.git/blob - src/game.rs
19940cd2566ba67e0cae7f2c31f16626f90fd834
[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,
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;
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) -> bool {
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 moved = false;
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 => {
110                             moved = true;
111                             break;
112                         }
113                         MergeResult::Replaced => {
114                             moved = true;
115                         }
116                         MergeResult::Blocked => break,
117                         MergeResult::Empty => {}
118                     }
119                 }
120             }
121         }
122
123         moved
124     }
125
126     pub fn get(&self, pos: Pos) -> &Field {
127         self.fields
128             .get(pos.x as usize)
129             .unwrap()
130             .get(pos.y as usize)
131             .unwrap()
132     }
133
134     pub fn spawn<R>(&self, rng: &mut R)
135     where
136         R: rand::Rng + ?core::marker::Sized,
137     {
138         if let Some(field) = self
139             .fields
140             .iter()
141             .flat_map(|v| v.iter())
142             .filter(|f| f.value() == 0)
143             .choose(rng)
144         {
145             *field.0.borrow_mut() = 1;
146         }
147     }
148 }