Fischer-random Chess: automated randomizer
Built by: Josh Lefevre, Nathan Serafin, & Alyssa Casamento
A brief overview
In the Spring semester of 2018, our team of Carnegie Mellon students was paired with a gentleman named Steve, to develop a device that would make an element of his daily life convenient and entertaining in some way. At our first design research and interview with Steve we had acknowledged his interest in the game of chess. We began to explore effective ways in which we could improve his quality of playing as an avid player and teacher. After numerous iterations of ideas, we decided to create an automated Fischer-random chess device. This device would help Steve increase the speed of initializing chess tournament games, and help him to teach Fischer Random chess to others.
In our 1st publicized iteration, we recorded our process and critique session with Steve, and others. The feedback we received from these events solidified our overall direction of the project and encouraged us to improve our initial design. We began creating our final product, with a more legible, compact and user friendly design. This post is a discussion of our process and design decisions that led to our final product for Steve.
What we built
What it does
Our project is a device that facilitates the initial set-up of the back row (king, queen, bishops, knights, and rooks) of a chess game in accordance with the rules of the Fischer-random variant. The device’s interface is simple to use. One presses a power switch on the device and then pushes the “randomize” button that chooses one of the 960 possible setup combinations of Fischer-random chess. A single standard chess icon lights up with a unique color near each backrow square to indicate where the piece should be placed on the chess board. A total of eight pieces are chosen during each randomization process.
Intended use
When Steve is looking to play a round of Fischer random chess, he can pull out this device and align it with the chessboard, using the notch. Once he has placed the device on the table, he can use the power button to turn the device on and then press the “randomize” button to display a new legal, randomized set up for Fischer-random. Then Steve can quickly set up his back row and begin playing chess.
How we got here
Our initial visit with Steve, had successfully produced plenty of ideas we could have used. Some of the original ideas centered around making the driving experience safer by keeping Steve alert at the wheel,a tracking device that would help him find his phone when it gets lost and numerous devices that Steve could use to teach other about the game of chess.
Originally we had focused on ways in which we could create a system to locate Steve’s phone more personally effective than the “find my iPhone app” . We considered creating a case made of led’s that would blink with the push of a wireless button. Steve currently doesn’t use a case because of the increase in size it adds so we continued to brainstorm ideas.
After some discussion among the group, we reflected what Steve would personally find interesting. We acknowledged that most of our conversation had revolved around Steve’s love for chess. He enjoys all aspects: playing, teaching, competing, and exploring new variations.
Our first chess ideas revolved around helping Steve teach others about the game of chess. We considered building a smart chess board that would show someone how to set up pieces and show all legal moves one to three moves in advance given a certain setup. We felt that this would allow Steve to teach students faster but also be a fun way to show how technology could be used to lower learning barriers.
After a few days of diagramming, consulting with aids, and wrapping our heads around the logic, we realized that this approach, while good, was both outside our computational and physical hardware abilities while remaining within the scope of the course, and ultimately not personally designed to help the need of our user;Steve,. Refocusing our goals on improving the game of chess for Steve led us to think about ways in which we could build a device that would allow for Steve to play one of his favorite versions of chess, Fischer-random. Considering Steve currently takes an unnecessary amount of time to randomize his Fischer Random setup, we explored ways in which we could efficiently scrambles the starting location of a players back row.
We created our first LED prototype that would demonstrate the basic idea behind the Fischer Random functionality.
It indicated where the randomized chess piece positions should be, as well as exemplifying our initial form concepts. We took this model to our critique with Steve and received an ample amount of feedback on how we could improve our design. This demonstration and critique of several ideas with Steve provided us a guiding star to follow throughout the rest of our design and making process.
While we were satisfied with the overall concept of the design , we brainstormed with Steve about ways in which we could most effectively display each piece.
Steve had suggested to use the typical chess piece symbols from the online versions of chess to ensure the images were legible.
We considered many display option including, RFID tagged pieces to LED backlit icons, LCD screens, laser-cut displays and many other ideas.
After much discussion and ideation, we narrowed our design to testing two different methods for displaying the the set up for the back row. The first idea explored was to implement backlit icons of the various chess pieces.Initially, we made great progress on integrating and programming the lights beyond the original concept idea we showed Steve during our critique. However, the idea of using LCD or OLED screens to display each random ordered piece icon seemed optimal . We were still leaning towards using LCD screens because the display size of icons would be larger and the form factor of the end device would be smaller and less bulky,
We switched gears and began working with some LCD displays using the SPI protocol. They were rather frustrating to use, due to non-ideal libraries, incompatibility with the Arduino Uno’s native 5V logic level, and an extreme lack of good bitmap-conversion tools.
After a week of sleepless nights, we decided to cut our losses and returned our focus to making the backlit LEDs the best we could make them.
We ran several experiments on how to make the back-lit chess icons look good and be legible to those with even compromisable eyesight. We ended up choosing a layered system of frosted or etched acrylic and birch plywood that would dilute the LEDs to create the effect and finish we sought.
Some complications we faced while making the device were that the lights were unevenly spaced on the LED light strand we were using, which made the placement of holes more difficult. We were also hampered by significant differences in the thickness of the plywood versus the acrylic, which nominally should have been the same.
The final difficulty was not the coding of the project, which came rather easily after thinking through the logic, but the assembly of the final product using glue, and mixed materials (acrylic and wood). We had quite a few material casualties, and the hand polishing took a long time.
After our final assembly we were excited to share this product with Steve.
At our presentation, he became excited and mentioned that this is exactly what he wanted. He also noted how helpful this device would be at his chess club and for tournament play. This was a the moment of excitement and personal reward for our team. We recognized that we had truly met his need and created meaningful gift for him to use, now and into the future.
We did not stick to our final build plan, as we found it very difficult do decide between using small displays, or the cutouts that we eventually settled upon. While diverging from the plan made the project more stressful, it did allow us to more carefully consider our options.
Conclusion & lessons learned
Critique major take-aways
There were three major suggestions that would have significantly improved the project. First, when the device is first powered up it could turn on all of the lights to their appropriate color, allowing easy verification that the device is fully functional. Second, a clear panel could be placed over the top of the device to protect from dirt and dust. Third, the lights and symbols could be mirrored on the other side of the device, and the device could be placed vertically in the center of the board, allowing both players to set up simultaneously.
- These suggestions paired with our own thoughts would lead us to develop future considerations for building a similar device the future:
- Go with name brand LED strips to ensure a greater likelihood of equidistant spacing between each light.
- Integrating high resolution OLED screens would be effectivtive and decreasing the size and form factor of the end product.
What we learned
Building a device for someone else is much more rewarding than building one for yourself.
If we were to do this project over again we’d involve Steve more frequently throughout the process and go beyond just having a critique but actually conduct user testing in real environments where the device will be used. Though we kept in contact with Steve via email periodically our actual interaction were limited which could have added more value to our finished project
Working with an older adult is rewarding and your realize that they have 88% of the same needs you do only they are little older and may not know all of the possible functionality. Which in many ways highlights a design principle of building for the need not the exotic, even when the exotic is super cool.
Reflection
I’d recommend that everyone should have a chance to make something for a friend that they’ve made who isn’t family or roommate and see how it changes their life and perspective on human relationships.
The most important and interesting part of this project wasn’t the coding and making of the end product but the process of how we got there and the friends we made along the way.
Building a project for a specific person puts a rather different perspective on the design. Choices need to be made to best cater to the requirements of the intended user, rather than always choosing the most technically elegant solution. I think the conflicts were generally resolved, but it did cause some dissonance in the design process.
Technical details
Code
/* * Copyright 2018, Alyssa Casamento, Josh LeFevre, Nathan Serafin. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <Adafruit_NeoPixel.h>; constexpr int TDATA_PIN = 2; constexpr int BDATA_PIN = 3; constexpr int BUTTON_PIN = 4; constexpr int SRAND_PIN = A0; constexpr uint32_t color(uint8_t r, uint8_t g, uint8_t b) { return (((uint32_t) r) << 16) | (((uint32_t) g) << 8) | ((uint32_t) b); } constexpr uint32_t OFF_COLOR = color(0, 0, 0); constexpr uint32_t READY_COLOR = color(0, 255, 0); constexpr uint32_t ROOK_COLOR = color(255, 0, 0); constexpr uint32_t KNIGHT_COLOR = color(0, 255, 0); constexpr uint32_t BISHOP_COLOR = color(0, 0, 255); constexpr uint32_t KING_COLOR = color(255, 255, 255); constexpr uint32_t QUEEN_COLOR = color(255, 0, 255); constexpr int NPIXELS = 24; constexpr int ROW_SIZE = 8; uint32_t READY_DISPLAY[] = {READY_COLOR, OFF_COLOR, OFF_COLOR, READY_COLOR, OFF_COLOR, OFF_COLOR, READY_COLOR, OFF_COLOR, OFF_COLOR, READY_COLOR, OFF_COLOR, OFF_COLOR, READY_COLOR, OFF_COLOR, OFF_COLOR, READY_COLOR, OFF_COLOR, OFF_COLOR, READY_COLOR, OFF_COLOR, OFF_COLOR, READY_COLOR, OFF_COLOR, OFF_COLOR}; typedef enum { EMPTY = 0, KING = 1, QUEEN = 2, ROOK = 3, BISHOP = 4, KNIGHT = 5, } piece_t; bool prev_button = 0; bool go = false; bool pattern = false; uint32_t tdisplay[NPIXELS]; uint32_t bdisplay[NPIXELS]; Adafruit_NeoPixel tstrip = Adafruit_NeoPixel(NPIXELS, TDATA_PIN, NEO_GRB + NEO_KHZ800); Adafruit_NeoPixel bstrip = Adafruit_NeoPixel(NPIXELS, BDATA_PIN, NEO_GRB + NEO_KHZ800); static void display_setup(Adafruit_NeoPixel& strip, uint32_t c[]) { strip.setPixelColor(0, c[0]); strip.setPixelColor(1, c[1]); strip.setPixelColor(2, c[2]); strip.setPixelColor(3, c[3]); strip.setPixelColor(4, c[4]); strip.setPixelColor(5, c[5]); strip.setPixelColor(6, c[6]); strip.setPixelColor(7, c[7]); strip.setPixelColor(8, c[8]); strip.setPixelColor(9, c[9]); strip.setPixelColor(10, c[10]); strip.setPixelColor(11, c[11]); strip.setPixelColor(12, c[12]); strip.setPixelColor(13, c[13]); strip.setPixelColor(14, c[14]); strip.setPixelColor(15, c[15]); strip.setPixelColor(16, c[16]); strip.setPixelColor(17, c[17]); strip.setPixelColor(18, c[18]); strip.setPixelColor(19, c[19]); strip.setPixelColor(20, c[20]); strip.setPixelColor(21, c[21]); strip.setPixelColor(22, c[22]); strip.setPixelColor(23, c[23]); strip.show(); } static void display_back_row(Adafruit_NeoPixel& tstrip, Adafruit_NeoPixel& bstrip, piece_t back_row[]) { for (int i = 0; i < ROW_SIZE; i++) { switch (back_row[i]) { case KING: tdisplay[i * 3] = KING_COLOR; tdisplay[i * 3 + 1] = OFF_COLOR; tdisplay[i * 3 + 2] = OFF_COLOR; bdisplay[i * 3] = OFF_COLOR; bdisplay[i * 3 + 1] = OFF_COLOR; bdisplay[i * 3 + 2] = OFF_COLOR; break; case QUEEN: tdisplay[i * 3] = OFF_COLOR; tdisplay[i * 3 + 1] = QUEEN_COLOR; tdisplay[i * 3 + 2] = OFF_COLOR; bdisplay[i * 3] = OFF_COLOR; bdisplay[i * 3 + 1] = OFF_COLOR; bdisplay[i * 3 + 2] = OFF_COLOR; break; case BISHOP: tdisplay[i * 3] = OFF_COLOR; tdisplay[i * 3 + 1] = OFF_COLOR; tdisplay[i * 3 + 2] = BISHOP_COLOR; bdisplay[i * 3] = OFF_COLOR; bdisplay[i * 3 + 1] = OFF_COLOR; bdisplay[i * 3 + 2] = OFF_COLOR; break; case KNIGHT: tdisplay[i * 3] = OFF_COLOR; tdisplay[i * 3 + 1] = OFF_COLOR; tdisplay[i * 3 + 2] = OFF_COLOR; bdisplay[i * 3] = KNIGHT_COLOR; bdisplay[i * 3 + 1] = OFF_COLOR; bdisplay[i * 3 + 2] = OFF_COLOR; break; case ROOK: tdisplay[i * 3] = OFF_COLOR; tdisplay[i * 3 + 1] = OFF_COLOR; tdisplay[i * 3 + 2] = OFF_COLOR; bdisplay[i * 3] = OFF_COLOR; bdisplay[i * 3 + 1] = ROOK_COLOR; bdisplay[i * 3 + 2] = OFF_COLOR; break; } } display_setup(tstrip, tdisplay); display_setup(bstrip, bdisplay); } void setup() { pinMode(TDATA_PIN, OUTPUT); pinMode(BDATA_PIN, OUTPUT); pinMode(BUTTON_PIN, INPUT_PULLUP); pinMode(SRAND_PIN, INPUT); randomSeed(analogRead(SRAND_PIN)); tstrip.begin(); bstrip.begin(); tstrip.setBrightness(64); bstrip.setBrightness(64); tstrip.show(); bstrip.show(); } void loop() { bool button = !digitalRead(BUTTON_PIN); if (button != prev_button) { delay(100); prev_button = button; if (button) { go = true; pattern = true; } } if (!go) { display_setup(tstrip, READY_DISPLAY); display_setup(bstrip, READY_DISPLAY); } else if (pattern) { piece_t back_row[8] = {EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY}; int K, Q, R1, R2, BI1, BI2; int color1_cnt = 4; // Even. int color2_cnt = 4; // Odd. /* Place king. */ if (((K = random(1, 6)) % 2) == 0) { color1_cnt--; } else { color2_cnt--; } /* Place first rook. */ if (((R1 = random(0, K - 1)) % 2) == 0) { color1_cnt--; } else { color2_cnt--; } /* Place second rook. */ if (((R2 = random(K + 1, 7)) % 2) == 0) { color1_cnt--; } else { color2_cnt--; } back_row[K] = KING; back_row[R1] = ROOK; back_row[R2] = ROOK; /* Place first bishop. */ BI1 = random(0, color1_cnt - 1); int n = 0; for (int i = 0; i < ROW_SIZE; i += 2) { if (back_row[i] == EMPTY) { if (BI1 == n) { back_row[i] = BISHOP; break; } n++; } } /* Place second bishop. */ BI2 = random(0, color2_cnt - 1); n = 0; for (int i = 1; i < ROW_SIZE; i += 2) { if (back_row[i] == EMPTY) { if (BI2 == n) { back_row[i] = BISHOP; break; } n++; } } /* Place queen. */ Q = random(0, 3); n = 0; for (int i = 0; i < ROW_SIZE; i++) { if (back_row[i] == EMPTY) { if (Q == n) { back_row[i] = QUEEN; break; } n++; } } /* Place knights. */ for (int i = 0; i < ROW_SIZE; i++) { if (back_row[i] == EMPTY) { back_row[i] = KNIGHT; } } display_back_row(tstrip, bstrip, back_row); pattern = false; } }
Leave a Reply
You must be logged in to post a comment.