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}