othello_agent/gameplay/
encoding.rs

1use std::collections::HashMap;
2
3use crate::gameplay::types::IBoard;
4
5use crate::gameplay::constants::INITIAL_BOARD;
6
7///
8///
9/// Encodes board into string
10///
11/// # Arguments
12///
13/// * `board` - board to encode
14/// * `code_chars` - string of code characters
15///
16/// # Note
17///
18/// Relies on there being 64 characters in the board
19pub fn string_from_board(board: IBoard, code_chars: &str) -> String {
20    // convert 2d array to 1d array
21    let board: Vec<u8> = board
22        .iter()
23        .flatten()
24        .map(|&i| i)
25        .collect();
26    // convert board to string and append the string 22 to the end
27    let joined_board =
28        board
29            .iter()
30            .map(|&i| i.to_string())
31            .collect::<String>() + "22";
32    let mut chunks = Vec::new();
33    let mut i = 0;
34    // split the string into chunks of 3
35    while i < joined_board.len() {
36        let end = std::cmp::min(i + 3, joined_board.len());
37        chunks.push(&joined_board[i..end]);
38        i += 3;
39    }
40    // convert each chunk to a number and get the character from the code chars
41    chunks
42        .iter()
43        .map(|x| {
44            let index = u8::from_str_radix(x, 3).unwrap() as usize;
45            code_chars.chars().nth(index).unwrap()
46        })
47        .collect()
48}
49
50///
51/// Decodes string into board
52///
53/// # Arguments
54///
55/// * `s` - string to decode
56/// * `code_char_hash` - hashmap of code characters
57///
58/// # Note
59///
60/// Relies on there being 64 characters in the board
61pub fn board_from_string(s: &str, code_char_hash: &HashMap<char, u8>) -> IBoard {
62    let mut board: Vec<u8> = s
63        .chars()
64        .flat_map(|x| {
65            let sum = 27 + *code_char_hash.get(&x).unwrap();
66            let mut base_3_string = format_radix(sum as u32, 3);
67            // clear whitespace
68            base_3_string.retain(|c| !c.is_whitespace());
69            // slice the last 3 characters
70            base_3_string = base_3_string[base_3_string.len() - 3..].to_string();
71            base_3_string
72                .chars()
73                .map(|c| c.to_digit(10).unwrap() as u8)
74                .collect::<Vec<_>>()
75        })
76        .collect();
77    board.truncate(64);
78    // convert to 2d array
79    let mut board_formatted: IBoard = INITIAL_BOARD;
80    for (i, &x) in board.iter().enumerate() {
81        board_formatted[i / 8][i % 8] = x;
82    }
83    board_formatted
84}
85
86pub fn create_code_char_hash(code_chars: &str) -> HashMap<char, u8> {
87    let mut code_char_hash = HashMap::new();
88    for (i, c) in code_chars.chars().enumerate() {
89        code_char_hash.insert(c, i as u8);
90    }
91    code_char_hash
92}
93
94fn format_radix(mut x: u32, radix: u32) -> String {
95    let mut result = vec![];
96
97    loop {
98        let m = x % radix;
99        x = x / radix;
100
101        // will panic if you use a bad radix (< 2 or > 36).
102        result.push(std::char::from_digit(m, radix).unwrap());
103        if x == 0 {
104            break;
105        }
106    }
107    result.into_iter().rev().collect()
108}
109
110#[cfg(test)]
111mod tests {
112    use crate::gameplay::{
113        constants::{ INITIAL_BOARD, CODE_CHARS },
114        encoding::{ create_code_char_hash, board_from_string },
115    };
116
117    use super::string_from_board;
118
119    #[test]
120    fn it_works() {
121        let board_str = string_from_board(INITIAL_BOARD, CODE_CHARS);
122        assert_eq!(board_str, "---------h-yq---------")
123    }
124    #[test]
125    fn can_encode_and_decode() {
126        let board_str = string_from_board(INITIAL_BOARD, CODE_CHARS);
127        let hashmap_chars = create_code_char_hash(CODE_CHARS);
128        let board_decoded = board_from_string(&board_str, &hashmap_chars);
129        assert_eq!(INITIAL_BOARD, board_decoded);
130    }
131}