Skip to content

Solitaire card game for iOS and Android using React Native, Expo, MobX and TypeScript.

License

Notifications You must be signed in to change notification settings

janaagaard75/desert-walk-solitaire

Repository files navigation

Desert Walk Solitaire

Solitaire card game implemented in React Native and written in TypeScript.

Website: desertwalk.janaagaard.com.

Running the app locally

Just type yarn start to install the Node modules and start the app.

This opens a browser window where you can select 'Start iOS Simulator'.

You can also install Expo Go on your devices and run the app through that app.

Assets

Updating the app

See the Upgrading an Expo app article.

Expo manages and upgrades the versions of known dependencies when upgrading Expo. It does not touch the unknown dependencies.

Known dependencies

  • @babel/core
  • @types/react-native
  • @types/react
  • babel-preset-expo
  • expo
  • expo-app-loading
  • expo-font
  • expo-screen-orientation
  • react-native-svg
  • react-native
  • react
  • typescript

Unknown dependencies

  • @babel/plugin-proposal-class-properties
  • @babel/plugin-proposal-decorators
  • @babel/preset-env
  • @typescript-eslint/eslint-plugin
  • @typescript-eslint/parser
  • eslint
  • eslint-config-prettier
  • eslint-plugin-react
  • expo-cli
  • mobx
  • mobx-react
  • prettier
  • tslib

Updating an existing version

See Publish an update.

Example:

eas update --branch production --message "Fixed a typo."

Deploying a new version

Changes to the app that do not change the dependencies can be deployed without going through the app stores. Upgrading the Expo SDK Expo SDK, and also in a handful more cases does, however, require deploying a new version through the app stores.

  1. Increment buildNumber and versionCode in app.json.
  2. Bump version in app.json and package.json.

Deploying to Apple's App Store

  1. Run yarn build-ios to queue a build.
  2. Run yarn eas submit --platform ios to submit the build the Apple's App tory.
  3. Go to appstoreconnect.apple.com.
  4. Add the app to TestFlight and invite users.
  5. Test the app using TestFlight.
  6. Create new version and submit for review.
  7. Answer Yes to 'Does this app use the Advertising Identifier (IDFA)'?, and check the three boxes except 'Serve advertisements within the app'. Source.

Certificate for push notifications: expo/turtle#62 (comment).

Deploying to Google's Play Store

TODO

Learnings

Adding the undo/redo feature was difficult, because it meant retrofitting the data model for this new feature. The data model has been cleaned up by switching to a @computed based model simplified things. There is, however, still some code leftover from the previous model, meaning that the currently solution could be simplified and optimized.

TypeScript does not catch all errors. There are no compiler errors when checking if two different classes are equal, even though the result will always be false.

Having singleton classes (Game, Settings, Deck and Grid) is the same as making every component a connected component, meaning the cleanliness of having pure components that only depend on their props is gone. Singleton classes make it harder to spot if the model is on the right track, since having a lot of the model globally available easily leads to spaghetti code. I would love to try to rewrite the game passing things around with props, to see if that helps.

Creating a good data model is difficult. It might be a good idea to move the dragged card from Game to GridState.

Creating meaningful names is a difficult but important task. PositionedCard has been renamed at least five times and ended up with this name nonetheless. When a class is renamed, there are often local variables left, that should also be renamed.

PositionedCards that contains the desk of 52 cards remain the same between moves. EmptyCells that contain the four empty cells is a new array for each move. I should have realized this much earlier.

Making Settings, Game, Deck and Grid singletons - and thus global - has lead to a spaghetti code architecture, but that might actually be the force of MobX: Embrace the spaghetti architecture, and avoid drowning in it.

Right now the models are separated from the views, but this actually not be a good idea where there is a one-to-one relation between model and view, as with for example the cards.

It is probably wise to go all in on MobX and remove all setState calls from the application. See 3 Reasons why I stopped using React.setState.

Designing for the iPhone X is tricky for two reasons: 1) it has rounded corners, so you can get buttons that are cut off and 2) it has a bar at the bottom of the screen to close the app. The design has to take account of both things manually.

It's very difficult to get the animation timings right. Clicking undo multiple times in a row should animate several cards at once.

The game started out by only allowing dragging cards around. While this had a nice 'this is a simulation of a real solitaire game' feel to it, it quickly got annoying to drag the cards around. Add the ability to click on the cards makes it possible to play the game a lot faster, and personally I am no longer dragging cards anymore. The animations are very important to let the user know what is going on.

It's a code smell that the CardCellPair interface is required. This may be because the inheritance in the model is wrong.

The Game class has become too big. It might be possible to extract some of the code into a Main class and to move some of the code to GridState.

It's okay to have observable values inside views. Too many things have been stored in Settings.

The syntax for observable variables is heavy.

Naming things is important as ever. A bug was created because targetableCells included in the cell being moved from to get the overlap calculations correct, but the name had not been updated.

The UI of a game is complicated because everything is custom made.

The ability to undo back and forth required a more complex model that originally anticipated. This hasn't been modelled as nicely as it could, and the increased complexity makes it difficult to make changes to the game without cause bugs. It would probably be worth the effort of starting over before adding major new features like difficulty selector.

I create a demo app where I tested a few of the animation setup. This revealed some major logical errors in the code that handles the animations and allowed me to clean up that part.

SafeAreaView reserves too much space at the bottom, so it doesn't look nice.

Things that I did not finish

  • Consider using this singleton pattern.
  • Remember the game state when the app is restarted.
  • Make sure everything looks alright in all resolutions.
  • Remove the text from the 'confirm restart dialog', so that the game only contains icons.
  • Better colors. Dark mode by default?
  • Game lost screen. How should it work, since it's always possible to undo a move?
  • Animate the cards when they are dealt.
  • A picture of a desert in the background of the cards?
  • Make the icons look more like buttons.
  • Small animation when a card is moved into the correct position. Could be a small shine effect like the one on records in the Moves app. Pixel art example: https://i.imgur.com/oLmT5Ot.gif.
  • Avoid passing inline functions as props: https://itnext.io/how-we-boosted-the-performance-of-our-react-native-app-191b8d338347#e92b.

About

Solitaire card game for iOS and Android using React Native, Expo, MobX and TypeScript.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published