• My initial project with Color TFT LCD w/ Touch Screen - PacMan game - did not move much forward, nor did my second one - DIY Marine GPS, see http://forum.espruino.com/conversations/­127039 and http://forum.espruino.com/conversations/­255759.

    First I wanted to get more field testing going for my direct implementation for resistive touch screen (see http://forum.espruino.com/conversations/­256122).

    So I came up with this little nice game... I started out with 4*4-1 pieces, and had it - with bare UI - up to 9*9-1 - just displaying the tiles area and just having logic for creating the 'orderly' board and moving tiles. My UI enhancements then cut me short with Out of Memory to a max of 5x5-1... even after getting rid of some nice code commenting. I will bring some of the comments back in this post.

    Two conclusions:

    1. Arrays were killers 40+ years ago, and still are today!
    2. Viva Pico! *I'm looking very much forward to a chip with more memory, seriously...

    There are good news though, despite the OutOfMemory incident: The TOUCHR module got hardened: n = n - 1 with n = number of bugs (n closing in on 0).

    Moving tiles around sometimes still freezes the system... no clue why, even after noticing that sometimes my TOUCHR module was not listening anymore (after telling twice to listen... fixed the code in the listen() method. If you use my code from the other post, fix it like this to ignore a listen() command while already listening(). (The faulty code did a partial toggle and left touch detection in a weird state):

      tr.listen = function(b) {
        tr.l = b; if (b) { if (!tr.w) { // <-- fix  
        pinMode(xn,"input_pulldown");
        digitalRead(xp);
        digitalWrite([yn,yp],3);
        tr.w = setWatch(tr.track,xp,{edge:rising, repeat:false});
      } } else { // <-- fix
        if (tr.w) { clearWatch(tr.w); tr.w = null; }
      } return tr;
    };
    

    To get a better handle on discovering a freeze, I put a setBusyIndicator(LED3); into the code to see if it is my listening or something else. I noticed, that when the system is frozen and needs either a hard-reset or power-cycle, it is busy and never goes to sleep again. It could be that some unchecked run away in array or most likely the graphics is messing with me... (after me messing with boundaries... sooooorrrrrrry! - Tschumpuhung - find the translation yourself).

    Back into the - Puzzle 16 - game: this is the code:

    // puzzle16_inlineRC.js (c) muet.com
    
    // puzzle 16 game
    
    // ********* following inline TOUCHR code is only part of attached file
    // var touchrModule = { connect: function(xn,xp,yn,yp,callback,C) {
    // ...
    
    var lcd, touchr, p16;
    
    SPI1.setup({sck:B3, miso:B4, mosi:B5, baud: 1000000});
    var lcd = require("ILI9341").connect(SPI1, B6, B8, B7, function() {
     lcd.clear();
    
    p16 = 
    { t: 0
    , ts: []
    , c: 0
    , cs: []
    , X: 240
    , Y: 320
    , x0: 0
    , y0: 0
    , xp: 240
    , yp: 320
    , x: 4
    , y: 4
    , xn: 4
    , yn: 4
    , xns: []
    , yns: []
    , m: 15 
    , bl: 2
    , bt: 40
    , br: 2
    , bb: 40
    , xe: 0
    , ye: 0
    , d: 60
    , clrs: [[0,0,0],[1,1,1],[1,1,0],[0,0,1]]
    , ps: []
    , ls: []
    , tpd: function(xt,yt) { var i,xr,yr,xo,yo,dp=this.d;
        if ((i =    (xt>=this.x0) && (xt<this.x0+this.xp)
                 && (yt>=this.y0) && (yt<this.y0+this.yp) )) {
          this.upd(Math.floor(getTime() - this.t),this.ts);
          var x = Math.floor((xr=(xt-(xo=this.x0+this.bl-1­)))/dp);
          var y = Math.floor((yr=(yt-(yo=this.y0+this.bt-1­)))/dp);
          if (y < 0) {
          } else if (y < this.y) {
            if ((x >=0) && (x < this.x)) {
              var p = this.ps[y][x], n = p[0];
              if ( n >= 0) {
                var ey = this.ls[this.m][0], ex = this.ls[this.m][1];
                var e = this.ps[ey][ex];
                if (ey == y) { this.c++;
                  this.mvx(y,x,e,ex,(ex<x) ? +1 : -1,yo,xo,dp);
                } else if (ex == x) {
                  this.mvy(y,x,e,ey,(ey<y) ? +1 : -1,yo,xo,dp);
                }
              }
            }
          } else { x = Math.floor(xr/((this.xp-2*2)/8));
            if        ((x ===  0) && (this.xn>3)) {
              this.xn--; this.upd(this.xn,this.xns);
            } else if ((x ==   1) && (this.xn<5)) { 
              this.xn++; this.upd(this.xn,this.xns);
            } else if ((x ==   2) && (this.yn>3)) { 
              this.yn--; this.upd(this.yn,this.yns);
            } else if ((x ==   3) && (this.yn<5)) { 
              this.yn++; this.upd(this.yn,this.yns);
            } else if (x>3&&x<6) { 
              this.scr(this.xn,this.yn);
            } else if (x>5&&x<8) { 
              this.lup(this.xn,this.yn);
            }
          }
        }
        return i;
      }
    // move horizontally +-x
    , mvx: function(y,x,e,ex,d,yo,xo,dp)  {
        var p, xp = xo + ex * dp, yp = yo + y * dp;
        this.c++; this.upd(this.c,this.cs);
        lcd.setFontVector(dp*0.6);
        while (ex != x + d) {
          if (ex == x) {
            p = this.ps[y][ex] = e;
            this.ls[this.m] = [y,ex];
          } else {
            p = this.ps[y][ex] = this.ps[y][ex + d];
            this.ls[p[0]] = [y,ex];
          }
          this.drp(p,xp,yp,xp+dp-1,yp+dp-1);
          ex += d; xp += d * dp;
        }
      }
    // move vertically +-y
     , mvy: function(y,x,e,ey,d,yo,xo,dp) {
        var p, xp = xo + x * dp, yp = yo + ey * dp;
        this.c++; this.upd(this.c,this.cs);
        lcd.setFontVector(dp*0.6);
        while (ey != y + d) {
          if (ey == y) {
            p = this.ps[ey][x] = e;
            this.ls[this.m] = [ey,x];
          } else {
            p = this.ps[ey][x] = this.ps[ey + d][x];
            this.ls[p[0]] = [ey,x];
          }
          this.drp(p,xp,yp,xp+dp-1,yp+dp-1);
          ey += d; yp += d * dp;
        }
      }
    // draw pieces
    , drw: function() {
        var p,x=this.x,y=this.y,d=this.d;
        var x1=this.x0+this.bl-1,y1=this.y0+this.bt-­1;
        var x2=this.x0+this.xp-this.br,y2=this.y0+th­is.yp-this.bb;
        this.clr(0); lcd.fillRect(x1,y1,x2,y2);
        y1=y1-d; y2=y1+d-1; lcd.setFontVector(d*0.6);
        for (var yx = 0; yx<y; yx++) {
          y1 += d; y2 += d; x1=this.x0+this.bl-1-d; x2=x1+d-1;
          for (var xx = 0; xx<x; xx++) {
            x1 += this.d; x2 += d; p = this.ps[yx][xx];
            this.drp(p,x1,y1,x2,y2);
          }
        }
      }
    // draw piece - 1..m*n-1(0..this.m-1=x*y-2) - empty: -1
    , drp: function(p,x1,y1,x2,y2) {
       var n=p[0],rgb = this.clr(1 + ((n<0) ? 0 : n % 2 + 1));
       if (n>=0) {
         lcd.fillRect(x1,y1,x2-1,y2-1);
         var c = (rgb[0]*4 + rgb[1]*2 + rgb[0])^7;
         lcd.setColor(c&4,c&2,c&1);
         var o = this.d * 0.0125;
         lcd.drawString(n+1,x1+o*((n>8) ? 1 : 20),y1+o*14);
       } else {
         this.clr(0);lcd.fillRect(x1,y1,x2-1,y2-1­);
       }
      }
    // line up pieces (value pair optional)
    , lup: function(x,y) {
        this.ein(false);
        this.inp(x,y);
        this.drw();
        this.ein(true);
      }
    // scramble pieces
    , scr: function(x,y) {
        this.ein(false);
        this.inp(x,y);
        var c,p1,p2,r1,r2,s=(this.m+1)*3;
        for (c=0; c<s; c++) {
          p1 = this.ls[r1 = Math.floor(Math.random() * this.m)];
          p2 = this.ls[r2 = Math.floor(Math.random() * this.m)];
          this.ls[r2] = p1; this.ls[r1] = p2;
        }
        for (c=0; c<=this.m; c++) {
          this.ps[this.ls[c][0]][this.ls[c][1]][0]­ 
            = (c<this.m) ? c : -1;
        }
        this.drw();
        this.ein(true);
      }
    // init pieces (value pair optional)
    , inp: function(x,y) {
        this.spl(x,y);
        var n = -1;
        this.ps = new Array(this.y);
        this.ls = new Array(this.m+1);
        for (var yx=0; yx<this.y; yx++) {
          this.ps[yx] = new Array(this.x);
          for (var xx=0; xx<this.x; xx++) { n++;
            if (n < this.m) {
              this.ps[yx][xx] = [n];
              this.ls[n] = [yx,xx];
            } else {
              this.ps[yx][xx] = [-1];
              this.ls[n] = [yx,xx];
            }
          }
        }
      }
    // set color
    , clr: function(iorgb) {
        var rgb=(isNaN(iorgb)) ? iorgb : this.clrs[iorgb];
        lcd.setColor(rgb[0],rgb[1],rgb[2]);
        return(rgb);
      }
    // update value as specified
    , upd: function(v,s) {
        lcd.setFontVector(s[0]); var xd = lcd.stringWidth(v);
        this.clr(0); lcd.fillRect(s[1],s[2],s[1]+xd,s[2]+s[0]­);
        this.clr(1); lcd.drawString(v,s[1],s[2]);
      }  
    // set pieces x*y, @ x0,y0 size area xp*yp px
    //     paddings left,top,right,bot, lcd X*Y px
    , spl: function(x,y,xp,yp,x0,y0,bl,bt,br,bb,X,Y­) {
        if (X && Y) { this.X = X; this.Y = Y; }
        if (x && y) { this.x=this.xn=x; this.y=this.yn=y; }
        if (xp && yp) { this.xp = xp; this.yp = yp; }  
        if (!isNaN(x0)) { this.x0=x0; }
        if (!isNaN(y0)) { this.y0=y0; }
        if (!isNaN(bl)) { this.bl=bl; }
        if (!isNaN(bt)) { this.bt=bt; }
        if (!isNaN(br)) { this.br=br; }
        if (!isNaN(bb)) { this.bb=bb; }
        var dxm = (this.xp - this.bl - this.br) / this.x;
        var dym = (this.yp - this.bt - this.bb) / this.y;
        this.d = Math.floor((dxm < dym) ? dxm : dym);
        this.m = this.x * this.y - 1;
      }
    // init
    // x*y pieces, xp*yp display area 
    , ini: function(x,y,xp,yp,x0,y0,bl,bt,br,bb,X,Y­) {
        this.spl(x,y,xp,yp,x0,y0,bl,bt,br,bb,X,Y­);
        this.inf();
        this.inp();
        this.drw();
        this.ein(true);
      }
    // init frame
    , inf: function() {
        var x1=this.x0,y1=this.y0;
        var x2=x1+this.xp-1,y2=y1+this.yp-1;
        this.clr(0); lcd.fillRect(x1,y1,x2,y2);
        this.clr(1); lcd.drawRect(x1,y1,x2,y2);
        var x=x1+2, y=y1+this.yp-this.bb+1;
        var s=Math.floor((x2-x1-2*2)/4), c = Math.floor(s/3);
        var f=(c<this.bb-2)?c:this.bb-2; lcd.setFontVector(f);
        lcd.drawString("-",x,y); x+=c;
        lcd.drawString(this.xn,x,y); this.xns=[f,x,y]; x+=c;
        lcd.drawString("+",x,y); x = x1+2+s;
        lcd.drawString("-",x,y); x+=c;
        lcd.drawString(this.yn,x,y); this.yns=[f,x,y]; x+=c;
        lcd.drawString("+",x,y); x = x1+2+s*2;
        lcd.drawString("SCR",x,y); x += s;
        lcd.drawString("ORD",x,y);
        x=x1+2; y=this.y0+2;
        f=(c<this.bt-2)?c:this.bt-2;
        lcd.drawString("Time:",x,y); x+=s;
        this.ts=[f,x,y]; x+=s;
        lcd.drawString("Mvs:",x,y); x+=s;
        this.cs=[f,x,y];
      }
    // enable input 
    , ein: function(b) {
        touchr.listen(b);
        if (b) { var s="0      ";
          this.upd(s,this.ts); this.upd(s,this.cs);
          this.c=0; this.t=getTime();
        }
      }
    };
      
      touchr = touchrModule.connect(C0,C1,C2,C3,functio­n(x,y,tr){
        if (!tr.t) { 
          if (p16.tpd(tr.x,tr.y)) { return; }
        }
       });
      setBusyIndicator(LED3);
    //     pieces 4*4, in 220*300 at 10,10
    //     paddings left 3,top 40,right 3,bot 40, lcd X*Y
      p16.ini(4,4,220,300,10,10, 3,40, 3,40);
     });
    

    The UI allows you to start over with a SCRambled or ORDerly game, starting with time 0 [seconds] and 0 moves. The top shows time in seconds and number of moves since game started. The values are updated on every move and any other tap in the board area. Tapping the 'empty tile' tapping the empty spot or a tile not sharing x or y axis with the empty spot does nothing but update time.

    Updating shows that a faster interface to the display controller is really a must... Some relieve is possible with using a faster - less nice - font, but may be nicer for small fonts on low resolution as shown in the 4th attached image. I'll sure look into both faster interfacing and faster font at one time.

    The game is highly configurable... you can run it down to a Nokia display... looking forward to see some one picking that up. http://forum.espruino.com/conversations/­257064

    I have not developed another input option yet to use when touch not available. But providing the x and y coordinates on a touch or click (with a mouse type cursor) gets you going. I was thinking of a 4-way five-button or joy-stick-with-click option. Moving around on the board could be done with a cursor - which uses save/restore curser image area (see )...speed - or more precisely - no-speed become though even more a drag for the user experience).

    All parameters can be passed at initialization. Only font-size is safe and parameter calculated. The 'safety' of the other parameters are up to you. I had started to implement constraints, but with running out of memory already for the UI, I dropped it.

    The board configuration has not to be square: everything from 3..5 x 3..5 goes.

    More in a following post.

    The attachment are:

    1. Ready to run code (puzzle16_inlineRC.js - ReducedComments)
    2. Clip with moves of tiles (P16moiving.mp4 - need to get a ...pod for my phone)
    3. 1st Image: showing 4x4 after startup
    4. 2nd Image: showing modified board - Puzzle25 - after tapping the +s to increase x and y each to 5 and SCR(amble)
    5. 3rd Image: showing a 4x4 in game
    6. 4th Image: a low resolution 3x5 in part of the display

    Enjoy!


    6 Attachments

  • Dropped comments and related code are the subjects of this post.

    Comment/code sketching the implementation design.

    p16 = // puzzle16 game (x=4,y=4) - x,y variable (screen size too)
          // - x*y-1 = m pieces with values 0..x*y-2 (regular piece) 
          // - empty slot is virtual piece w/ value -1 (empty piece)
          // - ps = y*x pieces arr of arrays [row=y][col=x] = [value]
          // - ls = x*y locations by value array [value] = [y,x]
    { h: "Puzzle 16"
    , t: 0
    , .....
    

    Comment/code sketching the main line tpd(x,y) method:

    // tapped on screen at pixel xp,yp
    //  ? i = inside of game board
    //    update time display
    //    x = piece x coord 0..x-1
    //    y = piece y coord 0..y-1
    //    ? top porch / banner menu (above pieces)
    //      handle top menu
    //    :? pieces reg y (above bottom porch / footer menu)
    //      ? pieces reg x
    //        p = piece @ x y, n = value of piece
    //        ? piece (not empty 'piece')
    //          ex ey = x y of empty piece
    //          ? ey of empty piece == y of piece
    //            move vertically +-y
    //          : (ex of empty piece == x of piece)
    //            move horizontally +-x
    //    : (bottom porch / footer menu (below pieces))
    //      handle bottom menu -+x, -+y, scramble, and 'orderly'
    //  return i (=true when inside game board and handled)   
    , tpd: function(xt,yt) { var i,xr,yr,xo,yo,dp=this.d;
        if ((i =    (xt>=this.x0) && (xt<this.x0+this.xp)
                 && (yt>=this.y0) && (yt<this.y0+this.yp) )) {
          this.upd(Math.floor(getTime() - this.t),this.ts);
          var x = Math.floor((xr=(xt-(xo=this.x0+this.bl-1­)))/dp);
          var y = Math.floor((yr=(yt-(yo=this.y0+this.bt-1­)))/dp);
          if (y < 0) {
          } else if (y < this.y) {
            if ((x >=0) && (x < this.x)) {
              var p = this.ps[y][x], n = p[0];
              if ( n >= 0) {
                var ey = this.ls[this.m][0], ex = this.ls[this.m][1];
                var e = this.ps[ey][ex];
                if (ey == y) { this.c++;
                  this.mvx(y,x,e,ex,(ex<x) ? +1 : -1,yo,xo,dp);
                } else if (ex == x) {
                  this.mvy(y,x,e,ey,(ey<y) ? +1 : -1,yo,xo,dp);
                }
              }
            }
          } else { x = Math.floor(xr/((this.xp-2*2)/8));
                  console.log(x);
            if        ((x ===  0) && (this.xn>3)) {
              this.xn--; this.upd(this.xn,this.xns);
            } else if ((x ==   1) && (this.xn<7)) { 
              this.xn++; this.upd(this.xn,this.xns);
            } else if ((x ==   2) && (this.yn>3)) { 
              this.yn--; this.upd(this.yn,this.yns);
            } else if ((x ==   3) && (this.yn<7)) { 
              this.yn++; this.upd(this.yn,this.yns);
            } else if (x>3&&x<6) { 
              this.scr(this.xn,this.yn);
            } else if (x>5&&x<8) { 
              this.lup(this.xn,this.yn);
            }
          }    
        } return i;
      }
    

    Comments added to code only available in this post:

    // .....
    p16 = // .....
          // ....
    { h: "Puzzle 16" // not used yet / anymore - default values:
    , t: 0     // getTime() of game begin
    , ts: []   // time [fontsize,x,y] display specs for upd(v,s) 
    , c: 0     // count of moves set to 0 at game begin
    , cs: []   // count [fontsize,x,y] display specs for upd(v,s) 
    , X: 240   // display size x - not used yet / anymore
    , Y: 320   // display size y- not used yet / anymore
    , x0: 0    // x / left in display of board top/left corner
    , y0: 0    // y / top in display of board top/left corner
    , xp: 240  // x board size in pixels
    , yp: 320  // y board size in pixels
    , x: 4     // x - horizontal - number of pieces (columns)
    , y: 4     // y - vertical - number of pieces (rows)
    , xn: 4    // x next for the next scramble or orderly setup
    , yn: 4    // y next for the next scramble or orderly setup
    , xns: []  // x next [fontsize,x,y] display specs for upd(v,s) 
    , yns: []  // y next [fontsize,x,y] display specs for upd(v,s)
    , m: 15    // max internal piece val 0..x*y-1, m = 'empty' piece 
    , bl: 2    // border left in pixels
    , bt: 40   // border top in px for time and moves display
    , br: 2    // border right
    , bb: 40   // border bottom for menu (+-x, +-y, SCR, ORD)
    , xe: 0    // x (column 0..x-1) position of empty slot (piece) 
    , ye: 0    // y (column 0..y-1) position of empty slot (piece)
    , d: 60    // piece with and height (is always calc'd on scr / ord
    , clrs: [[0,0,0],[1,1,1],[1,1,0],[0,0,1]] // rgb colors 0..3 used
    , ps: []   // pieces (piece itself if array with value as 1st elt)
    , ls: []   // locations of pieces by value (empty slot is last)
    // tapped on screen at pixel xp,yp
    //  .....
    

    So much so far...

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

Puzzle16+ Game on ILI9341 2.8" 262K Color TFT LCD w/ Resistive Touch Screen

Posted by Avatar for allObjects @allObjects

Actions