UPDATE (5May18) - I updated the way the app stores data. It now appends rows to a Google Sheet. It turns out you can't store data locally when the app is posted to ShinyApps. That makes this a bit harder to run locally. You will either have to build that in yourself or create a Google Sheet and add the key for that table to the global.R file.
This is a simple Rock, Paper, Scissors application built in R Shiny. The computer's selections are driven by a multiple logistic regression model.
You can preview the app at: https://brandonkopp.shinyapps.io/RPS-Shiny/.
I developed this app to show my coworkers something neat you can do with R Shiny. This app was also an opportunity for me to learn some new things. This is what I learned while creating this app and hope to pass on to anyone who comes across this repo:
- Persistent Storage: This is the first app I've built that collects and stores data. I originally tried using traditional saveRDS(), but found that that doesn't work on ShinyApps. The app now uses Google Sheets to store the data. This causes some latency issues, but does what it's supposed to.
- Predictive Modeling: I wanted to build a predictive model into an application. How the model works is explained below.
- Image Elements: I wanted to incorporate dynamic images that appear and disappear as part of the game.
- Resetting: Through building this app, I discovered the InvalidateLater() function which is tremendously useful for resetting the game board.
The heart of the app is the predictive model that drives the computer's choices. I started out with random selection, but that wasn't any fun. Why not record game data and put it to work. At first, I thought Rock, Paper, Scissors might not be the best game to try out a predictive model on. In order for a model to work, there has to be some pattern to pick up on and RPS is supposed to be random right? The more I played, the more I realized there probably is a pattern worth picking up and the computer has been beating me fairly regularly. Here's how it works:
Prediction: The computer uses multiple logistic regression to make a prediction about Player 1's next move. The prediction is then shifted on choice to the right (e.g., Rock becomes Paper) in order to counter the predicted move. There are three computer 'modes.' I hope to add more later.
- Random: As the name implies the computer selects randomly with all options having equal probabilities. The model always uses Random when there are fewer than 10 moves in the training dataset.
- Weighted: A multiple logistic regression is performed and the output probabilities for each of the options are used to weight the probability of selection of each option. This seemed like it made sense.
- Prediction: The same logistic regression as the Weighted mode is performed, but in this case, the option with the highest probability is selected and used as the prediction.
The Features: The logistic regression uses all of the previous matches as training data whether they were from the current player or not. I am considering adding an option to base predictions only on the current player, but I doubt a single player will play it long enough to train the model to recognize their specific patterns.
- Player 1 Choices: The last 4 choices by Player 1
- Computer Choices: The last 4 choices by the Computer
- Outcomes: The outcomes of the last 4 matches
- Time to make choice: The amount of time between the end of the previous round and the selection of an option by Player 1
- Number of Rock Uses: The cumulative number of times Player 1 used Rock
- Number of Paper Uses: The cumulative number of times Player 1 used Paper
- Number of Scissors Uses: The cumulative number of times Player 1 used Scissors
That's it. With each new match, the model runs again. I am interested to see at what point latency starts to become a real issue. How many matches can it cruch through before there starts to be some real performance consequences. I added a process timer that measures the time required to run the model. Perhaps at some point, I will add a criterion that says only use the last X number of matches to make predictions. Or I could learn how to simply update the model rather than run the full thing. I'm still learning.