DEV Community

Cover image for Free freecell
Matt Ellen-Tsivintzeli
Matt Ellen-Tsivintzeli

Posted on • Edited on

Free freecell

A long time ago in exactly the same galaxy I started to try to make Freecell, as a way to learn Angular 1.3.

I got so far and then I got distracted by other things, as is the way of side projects.

I had some free time recently (I know, I wasn't expecting it either) and so I thought I'd give it another shot.

I essentially started from scratch, because I'm no longer interested in Angular 1.3, and I tend to use VueJS for my web stuff if I need a framework.

To see the results, it's on github.io: click here to play freecell!

Ever since I learnt WPF a decade and a half ago, I have really liked the MVVM style of programming. VueJS allows for that style very easily, and even improves upon it, without the need for explicit events to update the UI.

This means the game logic is completely separate from the view logic, which made writing this game a breeze.

It wasn't all plane sailing, as I, for some reason, decided that I would store the cards in a 2D jagged array. Not a terrible idea, but each internal array is a column, and so when I was trying to get the cards to layout correctly in a CSS grid I couldn't just iterate over the outer then the inner one like

<template v-for="cardCol in game.table">
    <template v-for="card in cardCol">
Enter fullscreen mode Exit fullscreen mode

Because that would lay the columns out as rows. So I had to use indices (why doesn't VueJS start from 0???) rather than objects and loop over the external array on the inside loop:

<template v-for="rowi in game.getLargestColumnCount()">
  <template v-for="coli in game.table.length">
    <div v-if="game.table[coli-1].length == 0 && rowi == 1" :class="'card column'+coli+' freecell'">
      <img src="cards/blank.png" @click="game.selectDropClear(coli-1, rowi-1)" >
    </div>
    <div v-else-if="game.getCard(coli-1, rowi-1) != ''" :class="'card '+cardClass(coli-1, rowi-1)">
      <img :alt="cardToCardName(game.getCard(coli-1, rowi-1))" :src="'cards/'+game.getCard(coli-1, rowi-1)+'.png'" @click="game.selectDropClear(coli-1, rowi-1)">
    </div>
  </template>
</template>
Enter fullscreen mode Exit fullscreen mode

I think the other two bad design decision I've made are implementing autocomplete and having the click handlers just be one function in the game (i.e. the model), rather than in the view and have the view figure out which course of action to take.

Having the game decide which action the player means (i.e. select a card (or stack of cards), place cards on another stack or deselect cards) has led to some spaghetti code that I might want to refactor at a later date.

Initially I didn't want to implement autocomplete because I didn't want to think about the logic. But after playing a few games without it I got so bored tapping each card into the home row that I felt compelled to implement it.

I should have stuck to my guns because it is just bad. It's a huge chunk of code that initially caused a bunch of bugs and head scratching. It's not even fully automatic. On the other hand, now I don't have to do nearly as much tapping.

I did stick to my guns about not making the cards drag and dropable, because I made this mostly to play on my phone and my tablet, so tapping into place is just a lot easier UX-wise (at least my UX, ymmv).

Over all I'm happy with the outcome, even if there are a few bugs lingering just out of sight.

GitHub logo Mellen / freecell

An implementation of the card game Free Cell

Top comments (0)