Started to document the Grammar modules
Getty Ritter
7 years ago
| 3 | 3 | use std::hash::Hash; |
| 4 | 4 | use rand::{Rng, thread_rng}; |
| 5 | 5 | |
| 6 | /// Our indices here are two 32-bit signed values. | |
| 6 | 7 | type Index = (i32, i32); |
| 8 | /// A `SparseBoard` here is a board that may or may not have every | |
| 9 | /// tile filled in, which we represent as a hashmap from indices to | |
| 10 | /// cells. When we use a `SparseBoard` as the LHS of a rule, we try to | |
| 11 | /// match every cell provided, but don't bother matching the cells | |
| 12 | /// which aren't provided; when we use one as the RHS of a rule, we | |
| 13 | /// instead overwrite only those cells provided in the board. | |
| 14 | /// | |
| 15 | /// A `SparseBoard` has a conceptual center, which is the cell at | |
| 16 | /// index `(0, 0)`. Indices in a `SparseBoard` can be negative. | |
| 17 | type SparseBoard<Cell> = HashMap<Index, Cell>; | |
| 7 | 18 | |
| 19 | /// The most important parts of a `Rule` are its LHS and its | |
| 20 | /// RHS. These correspond respectively to the patterns which are | |
| 21 | /// matched, and the cells that are used to replace those | |
| 22 | /// patterns. Note that both of these are sparse boards: a pattern may | |
| 23 | /// be an arbitrary shape, and its replacement may replace all, some, | |
| 24 | /// or none of the cells matched by the LHS. | |
| 8 | 25 | pub struct Rule<Cell> { |
| 9 | 26 | pub rule_name: Option<String>, |
| 10 | 27 | pub requires: HashSet<Cell>, |
| 11 | pub lhs: HashMap<Index, Cell>, | |
| 12 | pub rhs: HashMap<Index, Cell>, | |
| 28 | pub lhs: SparseBoard<Cell>, | |
| 29 | pub rhs: SparseBoard<Cell>, | |
| 13 | 30 | } |
| 14 | 31 | |
| 15 | 32 | pub struct Board<Cell> { |
| 82 | 99 | } |
| 83 | 100 | |
| 84 | 101 | impl<Cell: Clone + Hash + Eq> Rule<Cell> { |
| 102 | /// Create a new `Rule` from two sparse grids of cells, | |
| 103 | /// corresponding respectively to the LHS and the RHS | |
| 85 | 104 | pub fn new( |
| 86 | lhs: HashMap<Index, Cell>, | |
| 87 | rhs: HashMap<Index, Cell>, | |
| 105 | lhs: SparseBoard<Cell>, | |
| 106 | rhs: SparseBoard<Cell>, | |
| 88 | 107 | ) -> Rule<Cell> { |
| 89 | 108 | let rule_name = None; |
| 90 | 109 | let requires = lhs.values().cloned().collect(); |
| 91 | 110 | Rule { rule_name, requires, lhs, rhs } |
| 92 | 111 | } |
| 93 | 112 | |
| 113 | /// Get mutable access to the LHS of the `Rule`, for modification | |
| 114 | /// later | |
| 115 | pub fn lhs_mut(&mut self) -> &mut SparseBoard<Cell> { | |
| 116 | &mut self.lhs | |
| 117 | } | |
| 118 | ||
| 119 | /// Get mutable access to the RHS of the `Rule`, for modification | |
| 120 | /// later | |
| 121 | pub fn rhs_mut(&mut self) -> &mut SparseBoard<Cell> { | |
| 122 | &mut self.lhs | |
| 123 | } | |
| 124 | ||
| 125 | /// Attempt to apply this rule to the provided board at random | |
| 126 | /// (i.e. if there are multiple possible applications of this | |
| 127 | /// rule, then it should choose one of them entirely at random | |
| 94 | 128 | pub fn apply_to_board(&self, b: &mut Board<Cell>) -> bool { |
| 129 | // for each random location in the board | |
| 95 | 130 | 'outer: for (x, y) in b.random_indices() { |
| 131 | // find out whether each of our tiles matche | |
| 96 | 132 | for (&(i, j), v) in self.lhs.iter() { |
| 133 | // if it doesn't, then skip ahead | |
| 97 | 134 | match b.get((x + i, y + j)) { |
| 98 | 135 | Some(r) if v == r => (), |
| 99 | 136 | _ => continue 'outer, |
| 100 | 137 | } |
| 101 | 138 | } |
| 102 | 139 | |
| 140 | // if all of them match, then do the rewrites! | |
| 103 | 141 | for (&(i, j), r) in self.rhs.iter() { |
| 104 | 142 | b[(x + i, y + j)] = r.clone(); |
| 105 | 143 | } |
| 106 | 144 | |
| 145 | // and because the rule applied, we can quit now | |
| 107 | 146 | return true; |
| 108 | 147 | } |
| 148 | // if we've tried them all and none of them worked, then we've | |
| 149 | // failed | |
| 109 | 150 | false |
| 110 | 151 | } |
| 111 | 152 | } |
| 112 | 153 | |
| 113 | 154 | |
| 155 | #[cfg(test)] | |
| 156 | #[test] | |
| 114 | 157 | pub fn grammar_test() { |
| 115 | 158 | let rule1: Rule<char> = Rule::new( |
| 116 | 159 | vec![ ((0, 0), 'x') ].into_iter().collect(), |
| 122 | 165 | ); |
| 123 | 166 | let mut board = Board::new(8, 8, '.'); |
| 124 | 167 | board[(2, 2)] = 'x'; |
| 125 | let _ = rule1.apply_to_board(&mut board); | |
| 126 | let _ = rule2.apply_to_board(&mut board); | |
| 168 | assert!(true, rule1.apply_to_board(&mut board)); | |
| 169 | assert!(true, rule2.apply_to_board(&mut board)); | |
| 127 | 170 | board.print(); |
| 128 | 171 | } |