It’s the Final Finale, Finally

Danny Brandt
5 min readSep 21, 2021

--

It’s time to wrap things up! My final project for Flatiron school!

React, Redux, Rails, SQL, async/await, HTML, CSS, and more! It’s all coming together on this project. Since early on in the program I’ve wanted to make a game. I love board games, and one that my partner and I have really been enjoying is DiceThrone (https://shop.dicethrone.com/). It’s fantastic! The game design, art, and packaging are to die for, so I wanted to pay homage to that by making some version of the game for my final project.

This game is like Yahtzee, but a fight. Players roll 5 dice up to 3 times trying to activate abilities for their character with different combinations of dice rolls(mainly straights or 3–5 of a kind). In the actual DiceThrone game, the player characters are asymmetrical and get a hand of cards with a variety of abilities, many of which allow you to change dice results! In my project, I simplified the rules. Players are symmetrical, and there are no dice or ability modifiers(yet).

This project taught me so much about data flow, asynchronous javascript, and Redux. I designed the backend to support one main feature, a ranking system. The only data that is saved is a username, wins, and losses, which then is used to calculate rank. Beyond that everything is handled in my Redux reducer and state. I kept the game logic focused on managing the initialDice variable (state), generating the rolls, and gathering user input. The initialGame state holds the player information (P1: and P2: are updated with the player object from the DB once logged in)and the game status i.e. ‘is it won?’, and ‘what did those dice just do?’. The initialUser state simply holds the username to create a new user and a key that holds an index of all the users. These states are managed by their respective reducers.

const initialDice = { rolls: 1, results: [0, 0, 0, 0, 0], clicked: [false, false, false, false, false] }const initialGame = { turn: 'P1', P1: "Player 1", P2: "Player 2", P1HP: 20, P2HP: 20, won: false, loggedIn: false, rollText: { type: "No Effect", effect: ":(" } }const initialUser = { username: '', allUsers: null }

I created routes using react-router for a homepage, sign up, log in, leaderboard, game page. I also initially made a loading page ‘/start’ to inform the user that their data was being fetched, this ended up not being needed, more on that later.

import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';import Login from './components/Login';import Signup from './components/Signup';import Ranks from './components/Ranks';import GameContainer from './containers/GameContainer';import HomePage from './containers/Homepage';import StartGame from './components/StartGame';function App() {return (<Router> <div className="App">   <Switch>      <Route exact path='/' render={() => <HomePage />} />      <Route exact path='/signup' render={() => <Signup />} />      <Route exact path='/login' render={() => <Login />} />      <Route exact path='/ranks' render={() => <Ranks />} />      <Route exact path='/game' render={() => <GameContainer />} />      <Route exact path='/start' render={() => <StartGame />} />    </Switch>  </div></Router>);}export default App;

In the gameContainer, the users have separate components displaying their username and hit points. Another component displays the dice and rolling buttons. Managing the placement of these components was not something I put much thought into until it was time to add some CSS, note to self: don't do that again. It took a bit of refactoring and hacky CSS to get the look that I was hoping for, kind of. I may go back and refactor with CSS Grid and make everything mobile-friendly, but it is WORKING, so moving on.

With the project coming together into something I could share and test with others I decided to have my partner try it out. I was really excited about this and was happy to answer any questions she had about what was going on. After explaining when she should click which buttons, one thing became clear to me; the user doesn’t know what the hell you are trying to do, you have to tell them, and then show them, and then offer a reminder if they still don’t get it.

So back to the ‘/start’ route and why that wasn’t necessary. I initially created it because of the asynchronous nature of fetching the individual users in the database. Its job was to show the user, we are looking for you in the DB just hang on a sec…. This worked but not very well. I found that the time variation for fetching was pretty big, and the way my Rails server interacted with my npm server was not always predictable. So my workaround was to fetch ALL of the users when the homepage mounted. I achieved this by setting an interval to attempt fetching the users and did not display and buttons to sign up, log in or start the game until the data was fetched. This worked great!

componentDidMount() {   this.fetchUsers();   this.timer = setInterval(() => {   this.fetchUsers()   console.log('Users added at mount', this.props.allUsers)}, 5000);}clearTimer = () => {   clearInterval(this.timer)   this.timer = null}async fetchUsers() {try {   const response = await fetch("http://localhost:3000/api/user")   const json = await response.json()   this.props.setUsers(json.users)   console.log('GET_USERS called from hompage')} catch (err) {   console.log(err)}}

The user sees the welcome screen and once the buttons display all of the interactions with the app are free from any ties to the DB. The only other time we make a call to the DB is after the game is completed, this needed to be asynchronous as well to avoid concurrent requests to my SQL database, which only supports one thread at a time. I did this with a basic async/await function, and for the second call, I wrapped it in a setTimeout() function to give the first request time to complete before sending another out.

updateAfterWin = async () => {await this.updateUser(this.props.P1)   setTimeout(() => {      this.updateUser(this.props.P2)      console.log('update in the timer')}, 2000);}

There are definitely things that could be improved with informing the user on what is going on in the background and restricting them from doing anything that would not work as expected until the data is fetched. I also will update this project with a rules page, that will be accessible from the homepage as well as during the game. This will explicitly describe the effects of every dice roll in one place. Currently, the only time the user sees what their roll does is after the roll is resolved. The damage or healing that takes place is shown on the screen with a description of the dice results and fades away after 4 seconds.

I had a lot of fun conceptualizing and building this project, and as always there are so many things I would do differently if I started from scratch again, but what I learned from building this app was invaluable.

This project has so many possible bells and whistles to add, and I plan to work on some of those features after graduation, but it was difficult to focus on the minimum viable product with so much potential! I have come to the same conclusion after every project: planning is essential, and getting the base product functional is the MOST IMPORTANT thing.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Danny Brandt
Danny Brandt

Written by Danny Brandt

I'm a musician, dad, and programmer. Writing helps me learn and blogging helps me write.

No responses yet

Write a response