DEV Community

Cover image for Building Sudoku in Vue.js - Part 2
Raymond Camden
Raymond Camden

Posted on • Originally published at raymondcamden.com on

Building Sudoku in Vue.js - Part 2

Earlier this week I blogged about my attempt to build a Sudoku game in Vue.js. At the time, I felt like I had done a good majority of the work, but that I was at a good stopping point to write it up and blog. Well last night I “finished” the app (to be clear, there’s absolutely room for polish) and I’m kind of embarrassed at how little I had left to do. I’m going to assume I’m just far more intelligent than I think and am an awesome coder despite failing the Google test more than once.

In this update I tackled three things:

  • Added the ability start a new game with a custom difficulty.
  • Marking incorrect entries. Which again is a personal preference, it wouldn’t be too hard to make this optional.
  • Added the ability to notice when you won.

Let me tackle each part separately. For difficulty, I began by adding the supported difficulty levels to my state:

difficulties: ["easy", "medium", "hard", "very-hard", "insane", "inhuman"],

Enter fullscreen mode Exit fullscreen mode

I then modified initGrid to handle an optional difficulty:

mutations: {
    initGrid(state, difficulty) {
        if(!difficulty) difficulty = state.difficulties[0];
        state.origString = sudokuModule.sudoku.generate(difficulty);

Enter fullscreen mode Exit fullscreen mode

Finally, over in my main App.vue, I added UI to render the difficulties and a button to start a new game. There’s no restriction on when you can do this. First the HTML:

<select v-model="difficulty"> 
<option v-for="(difficulty,idx) in difficulties" :key="idx">{{difficulty}}</option>
</select> <button @click="newGame">Start New Game</button>

Enter fullscreen mode Exit fullscreen mode

And here’s the code behind this.

import { mapState } from 'vuex';

import Grid from '@/components/Grid';

export default {
  name: 'app',
  components: {
    Grid
  },
  data() {
    return {
      difficulty: null
    }
  },
  computed: mapState([
    'difficulties', 'wonGame'
  ]),
  created() {
    this.$store.commit('initGrid');
    this.difficulty = this.difficulties[0];
  },
  methods: {
    newGame() {
      this.$store.commit('initGrid', this.difficulty);
    }
  }
}

Enter fullscreen mode Exit fullscreen mode

I’m using mapState to bring in the difficulties and then added a method, newGame, that calls initGrid with the selected difficulty.

Now let’s look at marking incorrect values. I modified setNumber in my store to simply check if the new value matches the solution value:

// highlight incorrect answers
if(x !== state.grid[state.selected.x][state.selected.y].solution) {
    row[state.selected.y].error = true;
} else {
    row[state.selected.y].error = false;
}

Enter fullscreen mode Exit fullscreen mode

Then in Grid.vue, I check for this value and apply a class:

<td v-for="(cell,idy) in row" :key="idy" 
:class="{ 
    locked: grid[idx][idy].locked, 
    selected:grid[idx][idy].selected,
    error:grid[idx][idy].error
}"
@click="setSelected(grid[idx][idy], idx, idy)"> {{ grid[idx][idy].value }}</td>

Enter fullscreen mode Exit fullscreen mode

Finally, to handle if you’ve won the game, I further modified setNumber by adding in this code:

/*
did we win? this feels like it should be it's own method
*/
let won = true;
for(let i=0;i<state.grid.length;i++) {
    for(let x=0;x<state.grid[i].length;x++) {
        if(state.grid[i][x].value !== state.grid[i][x].solution) won = false;
    }
}
if(won) state.wonGame = true;

Enter fullscreen mode Exit fullscreen mode

As the comment says, it really felt like this should be it’s own method. Looking over my code now, I’d probably consider moving my Sudoku “game” logic in it’s own file and keep my store focused on just the data. I say this again and again but I still struggle, or not struggle, but really think about, where to put my logic when it comes to Vue and Vuex. I love that Vue is flexible in this regard though!

The final part of handling “game won” logic is a simple conditional in the main component:

<div v-if="wonGame">
    <h2 class="wonGame">YOU WON!</h2>
</div>

Enter fullscreen mode Exit fullscreen mode

That’s pretty simple and could be much more exciting, but I’m happy with it. You can see the code at https://github.com/cfjedimaster/vue-demos/tree/master/sudoku. If you want to see it in your browser, visit https://sudoku.raymondcamden.now.sh/. Please let me know what you think by leaving me a comment below!

Header photo by Tienda Bandera on Unsplash

Top comments (3)

Collapse
 
yaireo profile image
Yair Even Or

I've built Sudoku game before, in 2011 (sudokubum.com) and I can tell it it's mostly pure javascript and working/manipulating arrays, not to mention the work needed to be done developing the engine which gives a difficulty rating to the generated boards (easy..difficult), that's a very tricky part. back in 2011 there were no online algorithms to be used and I had to create my own.

Took me 3 months to make this game.

Collapse
 
raymondcamden profile image
Raymond Camden

I was lucky to find a library that did all that hard work for me. :)

Collapse
 
raymondcamden profile image
Raymond Camden

Thank you!