life_backend/format/rle/
core.rs

1use anyhow::Result;
2use std::fmt;
3use std::io::Read;
4use std::str::FromStr;
5
6use super::{RleHeader, RleParser, RleRunsTriple};
7use crate::{Format, Position, Rule};
8
9/// A representation for RLE file format.
10///
11/// The detail of this format is described in:
12///
13/// - [Run Length Encoded - LifeWiki](https://conwaylife.com/wiki/Run_Length_Encoded)
14/// - [Golly Help: File Formats > Extended RLE format](https://golly.sourceforge.net/Help/formats.html#rle)
15///
16/// # Examples
17///
18/// Parses the given RLE file, and checks live cells included in it:
19///
20/// ```
21/// use std::fs::File;
22/// use life_backend::format::Rle;
23/// use life_backend::Position;
24/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
25/// let file = File::open("patterns/rpentomino.rle")?;
26/// let parser = Rle::new(file)?;
27/// assert!(parser.live_cells().eq([Position(1, 0), Position(2, 0), Position(0, 1), Position(1, 1), Position(1, 2)]));
28/// # Ok(())
29/// # }
30/// ```
31///
32/// Parses the given string in RLE format:
33///
34/// ```
35/// use life_backend::format::Rle;
36/// use life_backend::Position;
37/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
38/// let pattern = "\
39///     #N R-pentomino\n\
40///     x = 3, y = 3\n\
41///     b2o$2o$bo!\n\
42/// ";
43/// let parser = pattern.parse::<Rle>()?;
44/// assert!(parser.live_cells().eq([Position(1, 0), Position(2, 0), Position(0, 1), Position(1, 1), Position(1, 2)]));
45/// # Ok(())
46/// # }
47/// ```
48///
49#[derive(Clone, Debug)]
50pub struct Rle {
51    pub(super) header: RleHeader,
52    pub(super) comments: Vec<String>,
53    pub(super) contents: Vec<RleRunsTriple>,
54}
55
56// Inherent methods
57
58impl Rle {
59    /// Creates from the specified implementor of [`Read`], such as [`File`] or `&[u8]`.
60    ///
61    /// [`Read`]: std::io::Read
62    /// [`File`]: std::fs::File
63    ///
64    /// # Examples
65    ///
66    /// ```
67    /// use life_backend::format::Rle;
68    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
69    /// let pattern = "\
70    ///     #N T-tetromino\n\
71    ///     x = 3, y = 2\n\
72    ///     3o$bo!\n\
73    /// ";
74    /// let parser = Rle::new(pattern.as_bytes())?;
75    /// # Ok(())
76    /// # }
77    /// ```
78    ///
79    #[inline]
80    pub fn new<R>(read: R) -> Result<Self>
81    where
82        R: Read,
83    {
84        RleParser::parse(read)
85    }
86
87    /// Returns the width written in the pattern.
88    ///
89    /// # Examples
90    ///
91    /// ```
92    /// use life_backend::format::Rle;
93    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
94    /// let pattern = "\
95    ///     #N T-tetromino\n\
96    ///     x = 3, y = 2\n\
97    ///     3o$bo!\n\
98    /// ";
99    /// let parser = Rle::new(pattern.as_bytes())?;
100    /// assert_eq!(parser.width(), 3);
101    /// # Ok(())
102    /// # }
103    /// ```
104    ///
105    #[inline]
106    pub const fn width(&self) -> usize {
107        self.header.width
108    }
109
110    /// Returns the height written in the pattern.
111    ///
112    /// # Examples
113    ///
114    /// ```
115    /// use life_backend::format::Rle;
116    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
117    /// let pattern = "\
118    ///     #N T-tetromino\n\
119    ///     x = 3, y = 2\n\
120    ///     3o$bo!\n\
121    /// ";
122    /// let parser = Rle::new(pattern.as_bytes())?;
123    /// assert_eq!(parser.height(), 2);
124    /// # Ok(())
125    /// # }
126    /// ```
127    ///
128    #[inline]
129    pub const fn height(&self) -> usize {
130        self.header.height
131    }
132
133    /// Returns the rule.
134    ///
135    /// # Examples
136    ///
137    /// ```
138    /// use life_backend::format::Rle;
139    /// use life_backend::Rule;
140    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
141    /// let pattern = "\
142    ///     #N T-tetromino\n\
143    ///     x = 3, y = 2, rule = B3/S23\n\
144    ///     3o$bo!\n\
145    /// ";
146    /// let parser = Rle::new(pattern.as_bytes())?;
147    /// assert_eq!(parser.rule(), &Rule::conways_life());
148    /// # Ok(())
149    /// # }
150    /// ```
151    ///
152    #[inline]
153    pub const fn rule(&self) -> &Rule {
154        &self.header.rule
155    }
156
157    /// Returns comments of the pattern.
158    ///
159    /// # Examples
160    ///
161    /// ```
162    /// use life_backend::format::Rle;
163    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
164    /// let pattern = "\
165    ///     #N T-tetromino\n\
166    ///     x = 3, y = 2\n\
167    ///     3o$bo!\n\
168    /// ";
169    /// let parser = Rle::new(pattern.as_bytes())?;
170    /// assert_eq!(parser.comments().len(), 1);
171    /// assert_eq!(parser.comments()[0], "#N T-tetromino");
172    /// # Ok(())
173    /// # }
174    /// ```
175    ///
176    #[inline]
177    pub const fn comments(&self) -> &Vec<String> {
178        &self.comments
179    }
180
181    /// Creates an owning iterator over the series of live cell positions in ascending order.
182    ///
183    /// # Examples
184    ///
185    /// ```
186    /// use life_backend::format::Rle;
187    /// use life_backend::Position;
188    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
189    /// let pattern = "\
190    ///     #N T-tetromino\n\
191    ///     x = 3, y = 2\n\
192    ///     3o$bo!\n\
193    /// ";
194    /// let parser = Rle::new(pattern.as_bytes())?;
195    /// assert!(parser.live_cells().eq([Position(0, 0), Position(1, 0), Position(2, 0), Position(1, 1)]));
196    /// # Ok(())
197    /// # }
198    /// ```
199    ///
200    pub fn live_cells(&self) -> impl Iterator<Item = Position<usize>> + '_ {
201        self.contents
202            .iter()
203            .scan((0, 0), |(state_x, state_y), item| {
204                if item.pad_lines > 0 {
205                    *state_y += item.pad_lines;
206                    *state_x = 0;
207                }
208                if item.pad_dead_cells > 0 {
209                    *state_x += item.pad_dead_cells;
210                }
211                let output = (*state_y, *state_x, item.live_cells);
212                *state_x += item.live_cells;
213                Some(output)
214            })
215            .flat_map(|(y, x, num)| (x..(x + num)).map(move |x| Position(x, y)))
216    }
217}
218
219// Trait implementations
220
221impl Format for Rle {
222    fn rule(&self) -> Rule {
223        self.rule().clone()
224    }
225    fn live_cells(&self) -> Box<dyn Iterator<Item = Position<usize>> + '_> {
226        Box::new(self.live_cells())
227    }
228}
229
230impl fmt::Display for Rle {
231    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
232        const MAX_LINE_WIDTH: usize = 70;
233        fn convert_run_to_string(run_count: usize, tag_char: char) -> String {
234            if run_count > 1 {
235                let mut buf = run_count.to_string();
236                buf.push(tag_char);
237                buf
238            } else {
239                tag_char.to_string()
240            }
241        }
242        fn flush_buf(f: &mut fmt::Formatter, buf: &mut String) -> Result<(), fmt::Error> {
243            writeln!(f, "{buf}")?;
244            Ok(())
245        }
246        fn write_with_buf(f: &mut fmt::Formatter, buf: &mut String, s: &str) -> Result<(), fmt::Error> {
247            if buf.len() + s.len() > MAX_LINE_WIDTH {
248                flush_buf(f, buf)?;
249                buf.clear();
250            }
251            *buf += s;
252            Ok(())
253        }
254        for line in self.comments() {
255            writeln!(f, "{line}")?;
256        }
257        writeln!(f, "x = {}, y = {}, rule = {}", self.width(), self.height(), self.rule())?;
258        let mut buf = String::new();
259        for x in &self.contents {
260            for (run_count, tag_char) in [(x.pad_lines, '$'), (x.pad_dead_cells, 'b'), (x.live_cells, 'o')] {
261                if run_count > 0 {
262                    let s = convert_run_to_string(run_count, tag_char);
263                    write_with_buf(f, &mut buf, &s)?;
264                }
265            }
266        }
267        write_with_buf(f, &mut buf, "!")?;
268        flush_buf(f, &mut buf)?;
269        Ok(())
270    }
271}
272
273impl FromStr for Rle {
274    type Err = anyhow::Error;
275    #[inline]
276    fn from_str(s: &str) -> Result<Self, Self::Err> {
277        Self::new(s.as_bytes())
278    }
279}