• I started on a 2nd project as well, porting my Rubido game to the bangle js2. Rubdio is a little chinese checkers / solitaire game.

    The aim of the game in chinese checkers is to select a peg on the board and jump over another peg to land on an empty spot. When doing this the peg you jumped over will be removed from the board. You need to play the game in such a way that only one peg remains on the board at the end. Depending on the difficulty you had chosen this can be either (only) in the middle of the board or anywhere on the board. Also depending on the difficulty you had chosen you can either jump horizontally and vertically over pegs or diagonally as well.

    I made a little gif to show case it. It was initially made with dark theme in mind, but i modified the game to let it use 1 bpp images. Luckily the tiles were 16x16 so they are byte aligned for using the frame option op drawImage

    I had a hard time optimizing the game, it does not do full screen redraws, thats way too slow anyway, but it only redraws & clears certain parts. But the issue was not with the drawing i had, but calculating moves left after each move. Initially this took 5 seconds on the bangle js2, with same optimasations i managed to make it 3 seconds. Then i tried compiling certain function to C and reduced the slowness even more it now sits at about 800 - 1000ms after doing a move (including drawing) and i really have no idea how to improve that further. The "compile" statement in the beginning of a function really helps a lot. But i had an issue with arrays & objects with it where the compiler did not report a failure during compilation but the function just did not work as expected, but i managed to solve that by directly using the array to access things instead of returning an object from some other function call.

    Anyway without further waiting here is a little preview in the form of a gif

  • This looks great! It'd be good to see this in the app loader!

    Do you think you could come up with a small self-contained example bit of code that you could post up here that runs really slow like you mentioned? It might be we can find a way to make it faster.

    What do you mean by "Moves Left" - does it actually try and run through each move each time to see how many would be needed to finish?

    Just a thought but even without using compiled code, if you could split the code up such that it could run as a function that gets called multiple times, you could update the screen immediately, and then run the calculation in the background. If the user interacted with the Bangle to make another move you could just cancel, redraw with the new state and start again.

  • This looks great! It'd be good to see this in the app loader!

    Yes that's my aim, but i'm still working on it, i need to still add some options to make the input rects visible and make theming optional.

    Do you think you could come up with a small self-contained example bit of code that you could post up here that runs really slow like you mentioned? It might be we can find a way to make it faster.

    I'll have to check but it may not be easy to do this as i used classes and such that are intertwined with the checks

    What do you mean by "Moves Left" - does it actually try and run through each move each time to see how many would be needed to finish?

    Yes, after each "move" you have done, the game loops through all still available pegs using a double for loop (i have a two dimensional array to represent the playfield). And i have a Peg class which contains a canMoveToPostion function that will check if a certain Peg can move to a postion and return true or false. For each peg it then calls this functions with all possible move location per peg, these are 4 or 8 calls (moves) to that function depending on the difficulty (if diagonals are allowed). It has todo this for each peg on the board and then it will display value, but it also uses that value to determine if the game is over (moves left = 0, like no peg can move anywhere anymore)

    this is the current code:

    function movesLeft() {
      "compiled";
      let result = 0;
      pegsLeft = 0;
      let BoardPart;
      let Y, X;
      for (Y = 0; Y < NROFROWS; Y++) {
        for (X = 0; X < NROFCOLS; X++) {
          BoardPart = BoardParts.Items[Y][X];
          //print (BoardParts.Items[Y][X]);
          // if there is a boardpart on that X,Y Coordinate
          // check all direction if we can move to that if so increases the movesleft
          if (BoardParts.Items[Y][X]) {
            if (BoardParts.Items[Y][X].AnimPhase < 2) {
              pegsLeft++;
              result += CPeg_CanMoveTo(BoardParts.Items[Y][X], X + 2, Y, false);
              result += CPeg_CanMoveTo(BoardParts.Items[Y][X], X - 2, Y, false);
              result += CPeg_CanMoveTo(BoardParts.Items[Y][X], X, Y - 2, false);
              result += CPeg_CanMoveTo(BoardParts.Items[Y][X], X, Y + 2, false);
              result += CPeg_CanMoveTo(BoardParts.Items[Y][X], X + 2, Y - 2, false);
              result += CPeg_CanMoveTo(BoardParts.Items[Y][X], X + 2, Y + 2, false);
              result += CPeg_CanMoveTo(BoardParts.Items[Y][X], X - 2, Y + 2, false);
              result += CPeg_CanMoveTo(BoardParts.Items[Y][X], X - 2, Y - 2, false);
            }
          }
        }
      }
      return result;
    }
    

    Just a thought but even without using compiled code, if you could split the code up such that it could run as a function that gets called multiple times, you could update the screen immediately, and then run the calculation in the background. If the user interacted with the Bangle to make another move you could just cancel, redraw with the new state and start again.

    thats not a bad idea, but i'm not sure how easily i can adapt to this, you see i do a single frame game loop and everything gets done during that single frame as soon as thats over the game is idling, i don't have a loop that keeps getting called. If nothing needs drawing it doesn't do anything at all but i could give it a try by recalling the game loop if the calculation had not finished but i'll need to check how i can actually do this. When you say run the calculation in the background do you refer to something like threads or so ? Is that some special functionality in javascript / bangle ?

  • Hey gordon,

    I managed to implement the background counting of movesleft. I've used the setinterval function todo this. And now we can keep on playing immediatly after a move was done and it will update the movesleft value once it has done calculating them. I just call the GameLoop one more time to verify if it is a winning game and draw the results basically my code looks like this now :

    //procedure that starts the movesLeftIter function using an interval to loop over
    //all pegs
    function movesLeft() {
     if(movesLeftTimer)
        clearInterval(movesLeftTimer);
      movesLeftCount = 0;
      pegsLeft = 0;
      movesLeftX = 0;
      movesLeftY = 0;
      movesLeftTimer = setInterval(movesLeftIter, 0);
    }
    
    // procedure that calculates how many moves are possible in the current board state for 1 Peg
    // we can simply do this by checking all parts and see if they can move to all directions
    // the canmoveto method in CPegs is does all the checking
    function movesLeftIter() {
      //print (BoardParts.Items[Y][X]);
      // if there is a boardpart on that X,Y Coordinate
      // check all direction if we can move to that if so increases the movesleft
      if (BoardParts.Items[movesLeftY][movesLeftX­]) {
        if (BoardParts.Items[movesLeftY][movesLeftX­].AnimPhase < 2) {
          pegsLeft++;
          movesLeftCount += CPeg_CanMoveTo(BoardParts.Items[movesLef­tY][movesLeftX], movesLeftX + 2, movesLeftY, false);
          movesLeftCount += CPeg_CanMoveTo(BoardParts.Items[movesLef­tY][movesLeftX], movesLeftX - 2, movesLeftY, false);
          movesLeftCount += CPeg_CanMoveTo(BoardParts.Items[movesLef­tY][movesLeftX], movesLeftX, movesLeftY - 2, false);
          movesLeftCount += CPeg_CanMoveTo(BoardParts.Items[movesLef­tY][movesLeftX], movesLeftX, movesLeftY + 2, false);
          movesLeftCount += CPeg_CanMoveTo(BoardParts.Items[movesLef­tY][movesLeftX], movesLeftX + 2, movesLeftY - 2, false);
          movesLeftCount += CPeg_CanMoveTo(BoardParts.Items[movesLef­tY][movesLeftX], movesLeftX + 2, movesLeftY + 2, false);
          movesLeftCount += CPeg_CanMoveTo(BoardParts.Items[movesLef­tY][movesLeftX], movesLeftX - 2, movesLeftY + 2, false);
          movesLeftCount += CPeg_CanMoveTo(BoardParts.Items[movesLef­tY][movesLeftX], movesLeftX - 2, movesLeftY - 2, false);
        }
      }
      movesLeftX++;
      if(movesLeftX == NROFCOLS)
      {
        movesLeftY++;
        if((movesLeftY == NROFROWS) && (movesLeftX == NROFCOLS))
        {
          clearInterval(movesLeftTimer);
          prevMovesLeftCount = movesLeftCount;
          prevPegsLeft = pegsLeft;
          if(GameState == GSGAME)
            loop();
          return;
        }
        movesLeftX = 0;
      }
    }
    

    and that seems to work fine

  • That looks great!

    Looking at the first bit of code you have there I see you do:

    BoardPart = BoardParts.Items[Y][X];
    

    But then you do:

          if (BoardParts.Items[Y][X]) {
            if (BoardParts.Items[Y][X].AnimPhase < 2) {
              pegsLeft++;
              result += CPeg_CanMoveTo(BoardParts.Items[Y][X], X + 2, Y, false);
              result += CPeg_CanMoveTo(BoardParts.Items[Y][X], X - 2, Y, false);
              result += CPeg_CanMoveTo(BoardParts.Items[Y][X], X, Y - 2, false);
              result += CPeg_CanMoveTo(BoardParts.Items[Y][X], X, Y + 2, false);
              result += CPeg_CanMoveTo(BoardParts.Items[Y][X], X + 2, Y - 2, false);
              result += CPeg_CanMoveTo(BoardParts.Items[Y][X], X + 2, Y + 2, false);
              result += CPeg_CanMoveTo(BoardParts.Items[Y][X], X - 2, Y + 2, false);
              result += CPeg_CanMoveTo(BoardParts.Items[Y][X], X - 2, Y - 2, false);
            }
          }
    

    So you reference BoardParts.Items[Y][X] a lot - each time Espruino is having to do a lot of lookups to find BoardParts, Items, then Y and X. What if you just replaced all occurrences or BoardParts.Items[Y][X] with BoardPart (which should be the same?).

    That might make a reasonable difference to the speed as well

  • Hi gordon,

    Thats a left over from when having the function compile C Code.

    If i used the boardpart variable it did not work with compiled C code, it somehow gotten integer numbers back instead of the objects, but if i always referenced it using BoardParts.Items[Y][X] the compiled c code worked fine. Initially i had it using the boardPart variable but that no longer worked when compiled to C code.

    The current (new iter) implementation also no longer works when compiled to C Code so i just removed the "compiled" flag but i can indeed use boardPart again now.

    Will change it back thanks.

  • Just to let people know that the game is as good as finished, i just need to test it a bit more. I will do a pull request soon against the main repo, but in the mean time you can already get a development build from my github
    or you can test and view the code inside the the emulator (note: viewing options might not work inside the emulator as the rubido.settings.js file will not exist, unless you upload it to storage as well using the emulator

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

Porting my Rubido (playdate version) to Bangle JS2 (chinese checkers / solitaire game)

Posted by Avatar for joyrider3774 @joyrider3774

Actions