Skip to content

Commit

Permalink
Improve documentation prior submission.
Browse files Browse the repository at this point in the history
  • Loading branch information
JGNieto committed Mar 20, 2024
1 parent 1efc22b commit 50c0b97
Show file tree
Hide file tree
Showing 11 changed files with 129 additions and 64 deletions.
29 changes: 19 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,27 @@ One player has a "Hand" Mango Pi in their pocket, which is talking to the "Brain

We took this project as an opportunity to explore different ways for different computers to communicate. To that end, we established a Bluetooth connection between two Pi's, developed a protocol to speak reliably over that connection, communicated with Bluetooth modules over serial, and came up with a protocol to send information between the "Brain" Pi and the laptop running Stockfish over serial. Additionally, we worked on our graphical abilities by implementing a GUI, and our HCI abilities by deciding how the user (that is, the cheater) would interact with the device.

How to use:
- `make run` runs a program to send raw AT commands to the Bluetooth HC-05 module (this is mostly for testing).
- `make brain` runs the code that should run on the "Brain" Mango Pi, connected to the host laptop which itself runs Stockfishs.
- `make hand` runs the program that should run on the "Hand" Mango Pi, which the player would secretly have in their pocket.

## Member contribution

Javier:

- Bluetooth communication driver for HC-05 module
- Protocol to reliably send "events" over Bluetooth between two Pi's (we called it JNXU).
- Bluetooth communication driver for HC-05 module (`bt_ext.c`)
- Protocol to reliably send "events" over Bluetooth between two Pi's (`jnxu.c`).
- "Hand" Pi (`hand.c`)
- Chess GUI
- Servo (for buzzing), and rotary encoder peripherals
- Part of Chess GUI (`chess_gui.c`)
- Peripherals for Servo, which we used for buzzing (see `hand.c`), and rotary encoder (`re.c`).

Ellen:

- Stockfish engine (computer running Stockfish <-> pi via uart)
- Stockfish engine interface (`engine.py`)
- Receive commands from Stockfish on the Pi side (`chess.c`)
- "Brain" Pi (`brain.c`)
- Chess GUI
- Chess GUI (`chess_gui.c`)

## References

Expand All @@ -38,23 +44,26 @@ We used the following hardware:
- 2 Mango Pi's
- 2 HC-05 Bluetooth modules
- 1 laptop (for Stockfish)
- 1 screen to visualize the chessboard
- 1 servo (for buzzing the instructions to the cheater)
- 1 rotary encoder (for the player to encode the state of the game)
- 1 rotary encoder (for the cheater to encode the state of the game)

Although our Python code `engine.py` interfaces with the Stockfish engine, which is open source, all code in this repository is original.
Code sources:
- Our Python code `engine.py` interfaces with the Stockfish engine, which is open source
- We adapted Julie's ringbuffer to be able to store pointers (see `ringbuffer_ptr.c`)
- When writing the `bt_ext.c` module, we copied some code from Julie's `uart.c` module.

## Self-evaluation

The Pi's are able to talk to one another! We were able to get Bluetooth communication with two Pi's, the engine running on the computer, monitor displaying the GUI, and two peripheries all working live for the chess game. We didn't end up pursuing other tricks for Mango magic, but went deeper on the chess idea and are happy with how it turned out.

Moments we're proud of:

- Bluetooth connecting the two Pi's!
- Getting stockfish working! and seeing the moves appear live (e2e4 d7d5 e4xe5 moment... :D).
- Rotary encoder + cursor logic for highlighting which piece is being moved.

Moments of pain:
- Chess has sooo many weird rules we had to take into account, such as taking 'en passant,' castling and, worst of all, pawn promotion and underpromotion. We could probably write a dissertation on the problems of handling paw promotion but, to make a very long story short, this one edge case that happens in ~4.5% of games added around 500 lines of code in total, including a specific GUI and overhauling our communication protocol. Suffice to say, we chess rules perfection seriously!
- Chess has sooo many weird rules we had to take into account, such as taking 'en passant,' castling and, worst of all, pawn promotion and underpromotion. We could probably write a dissertation on the problems of handling paw promotion but, to make a very long story short, this one edge case that happens in ~4.5% of games added around 500 lines of code in total, including a specific GUI and overhauling our communication protocol. Suffice to say, we take chess rules perfection seriously!
- Bluetooth can sometimes become unreliable, so we had to include many checks to ensure the connection was still alive at any given time, including "ping" and "echo" packets we created.

## Photos
5 changes: 1 addition & 4 deletions brain.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#include "chess_commands.h"
#include "interrupts.h"
#include "jnxu.h"
#include "printf.h"
#include "malloc.h"
#include "timer.h"
#include "uart.h"
Expand Down Expand Up @@ -51,7 +50,6 @@ static void paint_cursor(void) {
int visual_cursor_y = CHESS_SIZE - module.cursor_y - 1;
#endif
chess_gui_draw_cursor(visual_cursor_x, visual_cursor_y, is_piece_moved);
// printf("Cursor: %d, %d\n", visual_cursor_x, visual_cursor_y);
}

static void update_cursor(void *aux_data, const uint8_t *message, size_t len) {
Expand Down Expand Up @@ -124,7 +122,7 @@ static void button_press(void *aux_data, const uint8_t *message, size_t len) {

char your_move[8];
chess_get_move(your_move, sizeof(your_move)); // get stockfish move
// printf("Your move: %s\n", your_move);

if (strcmp(your_move, "NOPE\n") != 0) {
chess_gui_update(opp_move, false);
chess_gui_update(your_move, true);
Expand All @@ -139,7 +137,6 @@ static void button_press(void *aux_data, const uint8_t *message, size_t len) {
}

module.state = (module.state + 1) % 5; // next state
// printf("State: %d\n", module.state);

if (module.state == LISTENING_X1) { // show selected on button bress
paint_cursor();
Expand Down
2 changes: 0 additions & 2 deletions bt_ext.c
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,6 @@ static uint8_t recv_uart(void) {
module.connected = false;
}

// TEST
#if BT_DEBUG == 1
printf("%c", byte);
#endif
Expand Down Expand Up @@ -312,7 +311,6 @@ void bt_ext_send_raw_byte(const uint8_t byte) {
while ((module.uart->regs.usr & USR_TX_NOT_FULL) == 0) ;
module.uart->regs.thr = byte;

// TEST
#if BT_DEBUG == 1
printf("%c", byte);
#endif
Expand Down
8 changes: 8 additions & 0 deletions bt_ext.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@
* flags in a protocol). The user can also call bt_ext_connected to check if the
* Bluetooth module is connected to a device and, if not, call bt_ext_connect
* again to try to connect.
*
* TRIGGERS:
* It is sometimes useful to receive an interrupt whenever a certain character
* is received. You can register a "trigger" with a particular function which
* will be called whenever a certain character is received. Since there is a
* risk of the ringbuffer storing incoming data filling up, there also exists
* a fallback trigger which will be called after BT_EXT_MAX_BYTES_NO_TRIGGER
* is exceeded.
*/

#include <stdbool.h>
Expand Down
25 changes: 16 additions & 9 deletions chess.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
/* Module for communicating to Stockfish `engine.py` via UART. */
/*
* Module for communicating to Stockfish `engine.py` via UART.
*
* See `engine.py` for details on how the protocol works.
*
* Author: Ellen Xu <[email protected]>
*/

#include "assert.h"
#include "chess.h"
Expand All @@ -15,9 +21,11 @@ static rb_ptr_t *rb;
void chess_get_move(char buf[], size_t bufsize) {
assert(bufsize >= 8);

// When we receive information from the host laptop, there is a chance that
// it might be a command, in which case we enqueue it and jump back to the
// top of the function.
char move[256];

/* Gets the move from Stockfish */
int i = 0;
char ch;

Expand All @@ -26,37 +34,36 @@ void chess_get_move(char buf[], size_t bufsize) {
ch = uart_getchar();
move[i++] = ch;

if (i >= 7 && move[0] != '/')
if (i >= 7 && move[0] != '/') // if not command and too long, break
break;
else if (i >= sizeof(move) - 1)
else if (i >= sizeof(move) - 1) // if command and too long for command
break;

} while (ch != '\n' && ch != '\0');

move[i] = '\0';

if (move[0] == '/') { // command
if (move[0] == '/') { // found command
char *cmd = malloc(i);
memcpy(cmd, move + 1, i);
rb_ptr_enqueue(rb, (uintptr_t)cmd);
printf("Enque command: '%s'\n", cmd);

// caller is expecting a move, so we jump back and see if we get one
i = 0;
goto was_command;
} else {
// copy the move to buffer
memcpy(buf, move, bufsize);
}
}

char *chess_next_command(void) {
uintptr_t ptr = 0;
rb_ptr_dequeue(rb, &ptr);
if (ptr)
printf("Deq command: '%s'\n", (char *)ptr);
return (char *)ptr;
}

void chess_send_move(const char* move) {
/* Sends a move to Stockfish (\n terminated)*/
uart_putstring("\nMOVE_BEGIN\n");
uart_putstring(move);
}
Expand Down
2 changes: 2 additions & 0 deletions chess.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
* Module for Stockfish. Communicates over UART with Python script running
* Stockfish on a powerful machine (the Mango Pi would struggle).
*
* See `engine.py` for details on how the protocol works.
*
* Author: Ellen Xu <[email protected]>
* Author: Javier Garcia Nieto <[email protected]>
*/
Expand Down
42 changes: 21 additions & 21 deletions chess_gui.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/*
* Module for chess GUI. Displays a chess board on the screen. Includes
* function to update UI based on new move.
* function to update UI based on new move, sidebar, handling of chess rules
* like passant, promotion and castling.
*
* Author: Ellen Xu <[email protected]>
* Author: Javier Garcia Nieto <[email protected]>
Expand Down Expand Up @@ -315,6 +316,9 @@ static void sidebar_draw(void) {

line += 3;

// We figured stats where getting in the way of mvoe history, so we have
// commented them out:

// draw_text_centered(
// "Stats:",
// SQUARE_SIZE * 8,
Expand Down Expand Up @@ -400,37 +404,33 @@ static void draw_promote(int cursor) {
"Queen"
};

if (0 <= cursor && cursor <= 3) {
int char_h = gl_get_char_height();

for (int i = 0; i < 4; i++) {
gl_draw_string(
SQUARE_SIZE * 8 + 5,
SQUARE_SIZE * 6 + (char_h + 5) * (i + 1),
PROMOTION_PIECES[i],
cursor == i ? GL_RED : SIDEBAR_FT);
}
} else {
gl_draw_rect(
SQUARE_SIZE * 8,
SQUARE_SIZE * 6,
SCREEN_WIDTH - SQUARE_SIZE * 8,
SCREEN_HEIGHT - SQUARE_SIZE * 6,
SIDEBAR_BG);
int char_h = gl_get_char_height();

for (int i = 0; i < 4; i++) {
gl_draw_string(
SQUARE_SIZE * 8 + 5,
SQUARE_SIZE * 6 + (char_h + 5) * (i + 1),
PROMOTION_PIECES[i],
cursor == i ? GL_RED : SIDEBAR_FT);
}
}

void chess_gui_promote(int cursor) {
draw_promote(cursor);
gl_swap_buffer();
draw_promote(cursor);
if (0 <= cursor && cursor <= 3) {
draw_promote(cursor);
gl_swap_buffer();
draw_promote(cursor);
} else {
chess_gui_sidebar();
}
}

void chess_gui_draw_cursor(int cursor_col, int cursor_row, bool is_piece_chosen) {
if (is_piece_chosen && !cursor.has_chosen) {
cursor.chosen_col = cursor.col;
cursor.chosen_row = cursor.row;
} else if (!is_piece_chosen && cursor.has_chosen) {
// edge case: easier to just redraw whole board
stale_everything();
}

Expand Down
4 changes: 4 additions & 0 deletions chess_gui.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
* Module for chess GUI. Displays a chess board on the screen. Includes
* function to update UI based on new move.
*
* For efficiency, the module does its best to only redraw parts of the
* chessboard that are stale by keeping track of updates. It also uses
* DOUBLEBUFFER to avoid artifacts on the screen.
*
* Author: Ellen Xu <[email protected]>
* Author: Javier Garcia Nieto <[email protected]>
*/
Expand Down
Loading

0 comments on commit 50c0b97

Please sign in to comment.