Skip to main content

chessnut_move/protocol/
led.rs

1// Copyright 2026 Daymon Littrell-Reyes
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//   http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Square-addressed LED patterns and their private wire encoding.
16
17#[cfg(doc)]
18use crate::protocol;
19use crate::protocol::wire::PackedSquares;
20use crate::protocol::{BOARD_STATE_LENGTH, SQUARE_COUNT, Square};
21
22/// A color supported by a Chessnut Move square LED.
23#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
24pub enum LedColor {
25  /// Turns the square LED off.
26  #[default]
27  Off,
28
29  /// Illuminates the square in red.
30  Red,
31
32  /// Illuminates the square in green.
33  Green,
34
35  /// Illuminates the square in blue.
36  Blue,
37}
38
39impl LedColor {
40  /// Returns the four-bit value used in packed LED commands.
41  const fn encoded(self) -> u8 {
42    match self {
43      Self::Off => 0,
44      Self::Red => 1,
45      Self::Green => 2,
46      Self::Blue => 3,
47    }
48  }
49}
50
51/// LED colors for all 64 squares in canonical A1-to-H8 order.
52///
53/// Pass a pattern to [`Command::set_leds`][protocol::Command::set_leds]
54/// to create the command written to the board.
55#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
56pub struct LedPattern {
57  squares: [LedColor; SQUARE_COUNT],
58}
59
60impl LedPattern {
61  /// Creates a pattern from [`LedColor`]s in canonical A1-to-H8 order.
62  ///
63  /// Index zero controls A1 and index 63 controls H8.
64  ///
65  /// # Examples
66  ///
67  /// ```
68  /// use chessnut_move::protocol::{LedColor, LedPattern, SQUARE_COUNT};
69  ///
70  /// let pattern = LedPattern::new([LedColor::Red; SQUARE_COUNT]);
71  /// assert_eq!(pattern, LedPattern::all(LedColor::Red));
72  /// ```
73  pub const fn new(squares: [LedColor; SQUARE_COUNT]) -> Self {
74    Self { squares }
75  }
76
77  /// Creates a pattern with the same [`LedColor`] on every square.
78  ///
79  /// # Examples
80  ///
81  /// ```
82  /// use chessnut_move::protocol::{File, LedColor, LedPattern, Rank, Square};
83  ///
84  /// let pattern = LedPattern::all(LedColor::Green);
85  /// assert_eq!(
86  ///     pattern.color_at(Square::new(File::H, Rank::Eight)),
87  ///     LedColor::Green,
88  /// );
89  /// ```
90  pub const fn all(color: LedColor) -> Self {
91    Self {
92      squares: [color; SQUARE_COUNT],
93    }
94  }
95
96  /// Returns the selected [`LedColor`] for a square.
97  ///
98  /// # Examples
99  ///
100  /// ```
101  /// use chessnut_move::protocol::{File, LedColor, LedPattern, Rank, Square};
102  ///
103  /// let pattern = LedPattern::all(LedColor::Blue);
104  /// assert_eq!(
105  ///     pattern.color_at(Square::new(File::A, Rank::One)),
106  ///     LedColor::Blue,
107  /// );
108  /// ```
109  pub const fn color_at(&self, square: Square) -> LedColor {
110    self.squares[square.index()]
111  }
112
113  /// Replaces the selected [`LedColor`] for one square.
114  ///
115  /// # Examples
116  ///
117  /// ```
118  /// use chessnut_move::protocol::{File, LedColor, LedPattern, Rank, Square};
119  ///
120  /// let square = Square::new(File::D, Rank::Five);
121  /// let mut pattern = LedPattern::default();
122  /// pattern.set_color(square, LedColor::Red);
123  /// assert_eq!(pattern.color_at(square), LedColor::Red);
124  /// ```
125  pub fn set_color(&mut self, square: Square, color: LedColor) {
126    self.squares[square.index()] = color;
127  }
128
129  pub(crate) fn encode(&self) -> [u8; BOARD_STATE_LENGTH] {
130    PackedSquares::encode(&self.squares, LedColor::encoded).into_bytes()
131  }
132}
133
134impl Default for LedPattern {
135  fn default() -> Self {
136    Self::all(LedColor::Off)
137  }
138}
139
140#[cfg(test)]
141mod tests {
142  use super::*;
143  use crate::protocol::{File, Rank};
144
145  #[test]
146  fn colors_are_accessed_by_square() {
147    let square = Square::new(File::C, Rank::Six);
148    let mut pattern = LedPattern::default();
149
150    pattern.set_color(square, LedColor::Blue);
151
152    assert_eq!(pattern.color_at(square), LedColor::Blue);
153  }
154}