Hello everybody, I'm here to post what I've spent the last two months working on: Solitaire!
I am aware that it already exists for this calculator, so this just more for fun and as a learning experience.

So far I have implemented:

    ▪Klondike with one card drawn per turn
    ▪Animations so that the cards move from one place to another when you move them.

I still plan to implement:

    ▪Other solitaire games starting with Spider, we will see from there
    ▪Saving the game and undo button
    ▪Better animations at the end of the game
    ▪Possibly an animated background by doing something fancy with the color pallet.

The code is available at https://github.com/GeometricParrot/Solitaire-CElection but this is my first project in C so it's pretty messy. If anyone has any tips that would be much appreciated as I am still learning.

For some reason that video won't preview, I don't know why. Permalink: https://github.com/GeometricParrot/Solitaire-CElection/blob/f3b4c6ee67d9d94c8222bf30651d054dea4b66cf/screenshots/record.png
I've made quite a few of these card solitaire games, so here are some tips:

The current size of your built binary is 24,335 B. My Calculation game is 11,876 B. These both have basically the same amount of art in them, so there must be an easy way to cut the size of your game pretty dramatically.

I think it's your graphics. Bundling blank_cardv2.png is the first obvious culprit. This is a 2.1 kB white rectangle. You can easily just draw it with a few calls to gfx_FillRectangle_NoClip(), gfx_HorizontalLine_NoClip(), and gfx_VerticalLine_NoClip(). The jack, king, and queen are symmetrical: you can flip sprites with gfx routines, so go ahead and keep only the top part. The king is the biggest, but all three can fit into a 35 by 26 pixel rectangle, saving you another 3.7 kB in assets. There's still a lot else you can do, but cutting the size of the binary by twenty-something percent is pretty nice IMO.

Why are target_y and target_x defined in struct Card? I have never needed a card to know where it is before. It's either somewhere on the table, in which case you know where it is when you make the call to draw it, or being animated, in which case (as far as I can see) you can store that information in struct Animation. The advantage here is that Animations are transient; you only have 13 of them, while you have 52 cards. So this reduces the runtime size of the program, and allows it to run on busier hardware without allocation routines failing. (Also, if life_remaining gets booted, it lets you make struct Card a one-byte bitfield: 2 bytes for suit, 1 byte for face-up/down, 4 bytes for value.)

For the cs_shuffle routine, you can do it more simply: Fisher-Yates is the algorithm to look at, and it guarantees a fair shuffle in 51 (possibly OBOE) passes instead of hoping for one in 255 ;)

Also, what is the actual point of the singleton struct State? Why do you need multiple States? This isn't a criticism or a suggestion, I just want to understand the design philosophy. I would probably have just put all these variables loose instead of bundling them and passing them around as an argument.

I am really looking forward to see you implement other rule sets! A "generic" solitaire game with dynamic rules (i.e. a menu to select which game) is something that I've designed and redesigned several times, but never released. I would love to see which approach you take!
Mr Green Thank you very much for checking out my project! I haven't worked on it since I last posted because I lost interest and it looked like no one was interested. I saw your own card games before I made mine, so thank you very much for your advice.

I have some explanations for making the choices I did when I wrote this. At least it's what made sense in my head at the time.

For the graphics: Yes, I could have saved a lot of space by removing blank_cardv2.png and using built-in functions instead. I chose to have a dedicated background sprite because I think it would be cool in the future to make a Balatro port to calculator and this project was to test my ability with a simpler card game. I don't know if you are familiar with the game, but Balatro has different types of cards including Gold, Steel, Glass, etc. Balatro draws it's cards by having a sprite for the base card then drawing the glyphs/numbers on top of that. I wanted to be able to reuse this code for such a future project. As for the face cards I am aware of the symmetry, but couldn't decide between RLET sprites that I couldn't rotate or normal transparent sprites that I could rotate. I figured I would decide later so I just put in something basic and moved on to other parts of the program.

Quote:
Why are target_y and target_x defined in struct Card? I have never needed a card to know where it is before.

That is an excellent question! Originally I did store card data exactly as you describe in uint8_t's. But I was having a hard time figuring out how to know both where a card used to be and where it should be in order to animate the movement. I settled on cards remembering where they were last rendered, then when I go to render them again, I check if the new position is different. If it is, then I render it as an animation instead. The card also has a lifetime value so I know how many frames I should skip rendering the card because it is actively being animated. I know this uses way more memory but it is what seemed simplest at the time. There is definitely room to improve with that.

Quote:
For the cs_shuffle routine, you can do it more simply: Fisher-Yates is the algorithm to look at,

Yes, I saw the Wikipedia article for that while programming. I will most certainly incorporate this next time I work on it, but my own shuffler was just a hack to get something different every time I loaded a game. It stayed in because it was always just "good enough" for debugging.

Quote:
Also, what is the actual point of the singleton struct State?

Because I felt like it was kind of a nice way to package up everything I wanted to pass around in the main game loop. This was my first project in pure C. (Although I have done a little before with C++.) I don't need multiple States, it was just how I decided to do it without using global variable because they aren't recommended. There isn't much reason behind it, and I'm open to better ways of storing and passing around data.

Quote:
I am really looking forward to see you implement other rule sets! A "generic" solitaire game with dynamic rules (i.e. a menu to select which game) is something that I've designed and redesigned several times, but never released. I would love to see which approach you take!

Thank you, and I appreciate the support! Any update aren't going to be for a while though, as this project has taken a back seat. I'll be working on my submission to the dungeon crawler contest
  
Register to Join the Conversation
Have your own thoughts to add to this or any other topic? Want to ask a question, offer a suggestion, share your own programs and projects, upload a file to the file archives, get help with calculator and computer programming, or simply chat with like-minded coders and tech and calculator enthusiasts via the site-wide AJAX SAX widget? Registration for a free Cemetech account only takes a minute.

» Go to Registration page
Page 1 of 1
» All times are UTC - 5 Hours
 
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

 

Advertisement