Card Game

HTML と JavaScript でカードゲームのクラスファイル(card.js)を作成します。
card.js を組み込めばトランプや花札などのカードゲームの作成が簡単になります。
HTML のソースコードは、[F12]キーから[デバッガー]タブで確認することが出来ます。
2020/07/28 Windows10 & Microsoft Edge と Google Chrome で実行を確認しましたが、ブラウザによっては動かないことがあるかも知れません。
  1. CARD Class
    1. CARD Class を定義した card.js のソースコードです。
      function CARD(img, sp_w, sp_h, img_w, img_h)
      {   this.img = img;             //Image File path
          this.sw = sp_w;             //Sprite の幅(51)
          this.sh = sp_h;             //Sprite の高さ(75)
          this.wn = img_w / this.sw;  //Sprite の横分割数(10)
          this.hn = img_h / this.sh;  //Sprite の縦分割数(5)
      
          //「上, 右, 下, 左」の順に指定
          this.Clip = function(num, x, y)
          {  var xp = (num%this.wn)*this.sw;
             var yp = Math.floor(num/this.wn)*this.sh;
             var s1 = 'clip:rect(' + yp + 'px,' + (xp+this.sw) + 'px,' + (yp+this.sh) + 'px,' + xp + 'px);';
             return s1;
          }
          // 描画座標の計算
          this.Position = function(num, x, y)
          {  var xp = (num%this.wn)*this.sw;
             var yp = Math.floor(num/this.wn)*this.sh;
             var s2 = 'position:absolute;left:' + (x-xp) + 'px;top:' + (y-yp) + 'px;"';
             return s2;
          }
          // Sprite View
          this.View = function(num, x, y)
          {  var s = '<img src="' + this.img + '" style="' + this.Clip(num, x, y) + this.Position(num, x, y) + '>';
             document.write(s);
          }
          // Click Function
          this.Func = function(num, x, y, id, func)
          {  var s3 = ' id="' + id + '" onclick=' + func + '("' + id + '")';
             var s = '<img src="' + this.img + '"' + s3 + ' style="' + this.Clip(num, x, y) + this.Position(num, x, y) + '>';
             document.write(s);
          }
          // 左上座標を求める(描画基点(xp,yp)  描画間隔(xs,xs)  1行のカード数(xn))
          this.PosXY = function(num, xp, yp, xs, ys, xn)
          {  var s = 'position:absolute;left:' + (((num%xn)*xs)+xp) + 'px;top:' + ((Math.floor(num/xn)*ys)+yp) + 'px;';
             return s;
          }
          //カードをシャッフルする
          this.Suflle = function(n)
          {   var t = Array();
              var i, j;
              for(i=0; i<n; i++)     t[i]= -1;
              for(i=0; i<n; i++)
              {   j= Math.floor(Math.random()*n);
                  for(; t[j]!=-1; j=(j+1)%n);
                  t[j]= i;
              }
              return t;
          }
      }
      
    2. CARD Class の説明です。
      Constructor で画像を切り分けて描画するために必要な情報を受け取ります。
      カッコ内の数字は、花札の画像を使ったときの参考値です。
          this.img = img;             //Image File path
          this.sw = sp_w;             //Sprite の幅(51)
          this.sh = sp_h;             //Sprite の高さ(75)
          this.wn = img_w / this.sw;  //Sprite の横分割数(10)
          this.hn = img_h / this.sh;  //Sprite の縦分割数(5)
      
    3. Clip() 関数で画像を切り分けて Sprite を描画するときの矩形領域を計算します。
      Sprite の詳しい説明は CSS Sprite を参照して下さい。
    4. Position() 関数で Sprite を描画する X,Y 座標を計算します。
      元の画像(切り分ける前の画像)の左上を基点として計算するので、普通の X,Y 座標とは異なります。
    5. View() 関数が一枚のカードを描画する関数です。
    6. Func() 関数は画像の描画に加えて、クリックしたときに呼び出される関数を設定します。
      id はクリックされた画像を識別するためのIDです。
    7. PosXY() 関数は、一般の画像を描画するときの X,Y 座標の計算です。
      描画基点と描画間隔と1行のカード数から普通に計算します。
    8. Suflle() 関数がカードをシャッフルする関数で n で枚数を受け取ります。
      シャッフルした配列を関数値としてリターンします。
  2. シャッフルして並べる。(card_view.html)
    カードを並べる
    1. カードをシャッフルして並べて描画するソースコードです。
      head で "card.js" を組み込みます。
      <script src="card.js">
      </script>
      </head>
      
    2. body で new CARD() で CARD Class を生成します。
      card.Suflle(48) で花札(48枚)のカードをシャッフルします。
      シャッフル情報が設定されている t を参照して、カードを並べて描画します。
      Sprite のサイズは 51*75 ですが、53*77 の間隔で描画しています。
      <script type="text/javascript">
          var card= new CARD("img/hanafuda.gif", 51, 75, 510, 375);
          var i;
          var t = Array();
          t = card.Suflle(48);
          for(i=0; i<48; i++)
          {   card.View(t[i], i%12*53+10, Math.floor(i/12)*77+60);
          }
      </script>
      </body>
      
  3. 手札を重ねて描画する。(card_7.html)
    手札を重ねる
    1. プレーヤーに配られた7枚の手札を重ねて描画します。
      カード全体を画面に描画すると、スペースがきゅうくつになります。
      そこでカードが識別できる範囲でカードを重ねて描画します。
      <script type="text/javascript">
          var card= new CARD("img/hanafuda.gif", 51, 75, 510, 375);
          var i;
          var t = Array();
          t = card.Suflle(48);
          for(i=0; i<7; i++)
          {   card.View(t[i], i*25+100, 120);
          }
      </script>
      </body>
      
  4. クリックされた番号を確認する。(card_click.html)
    カードのクリック
    1. カードを並べて描画して、クリックされたカード番号を確認します。
      <script type="text/javascript">
        document.onmousedown =
          function(e)
          { if (!e)  e= window.event;
            var xp= e.clientX;
            var yp= e.clientY;
            var num = Math.floor((yp-60)/77)*12+Math.floor((xp-10)/53);
            window.alert("XP:" + xp + "  YP:" + yp + "  NUM:" + num);
          }
          var card= new CARD("img/hanafuda.gif", 51, 75, 510, 375);
          var i;
          var t = Array();
          t = card.Suflle(48);
          for(i=0; i<48; i++)
          {   card.View(t[i], i%12*53+10, Math.floor(i/12)*77+60);
          }
      </script>
      
    2. マウスをクリックすると document.onmousedown = の関数が呼ばれます。
      クリックされた座標からカード番号を計算します。
      Sprite は 10*60 を基点に、53*77 の間隔で描画されています。
      花札の画像は、12列, 4行に並んでいます。
          var num = Math.floor((yp-60)/77)*12+Math.floor((xp-10)/53);
      
  5. クリックで裏返す。(card_reverse.html)
    カードを裏返す
    1. カードを並べて、クリックで裏返します。
      <script type="text/javascript">
        document.onmousedown =
          function(e)
          { if (!e)  e= window.event;
            var xp= e.clientX;
            var yp= e.clientY;
            var num = Math.floor((yp-60)/77)*12+Math.floor((xp-10)/53);
            card.View(48, num%12*53+10, Math.floor(num/12)*77+60);
          }
          var card= new CARD("img/hanafuda.gif", 51, 75, 510, 375);
          var i;
          var t = Array();
          t = card.Suflle(48);
          for(i=0; i<48; i++)
          {   card.View(t[i], i%12*53+10, Math.floor(i/12)*77+60);
          }
      </script>
      
    2. カードをクリックすると、document.onmousedown = function(e) が呼ばれます。
      クリックされた座標を特定して、カードの番号を計算します。
      card.View() 関数で、裏面(48)のカードを描画します。
            var num = Math.floor((yp-60)/77)*12+Math.floor((xp-10)/53);
            card.View(48, num%12*53+10, Math.floor(num/12)*77+60);
      
    3. クリックされたカードは裏返るのですが、表面のカードは全て消去されます。
      それは、裏面(48)のカードが現在のページでは無く、新しく作成されたページに表示されるからです。
      ブラウザのページ更新アイコンをクリックすると、元のページに戻ります。
  6. ページを変えずに裏返す。(card_imgfunc.html)
    ページを変えずにカードを裏返す
    1. 新しいページを作らないで、現在のページでカードを裏返します。
      <script type="text/javascript">
      function ImgClick(num)
      {   document.getElementById(num).src= "img/hana_ura.gif";
          var s2 = card.PosXY(num, 10, 60, 53, 77, 12);
          document.getElementById(num).style= s2;
      }
          card= new CARD("img/hanafuda.gif", 51, 75, 510, 375);
          var t = Array();
          t = card.Suflle(48);
          for(var num=0; num<48; num++)
          {   card.Func(t[num], num%12*53+10, Math.floor(num/12)*77+60, num, "ImgClick");
          }
      </script>
      
    2. 現在のページでカードを裏返すには、カードのクリックを検出して ImgClick() 関数を呼び出します。
      ImgClick() 関数では、現在のページで使われているドキュメント要素を getElementById() で取得して、直接リソースを置き換えます。
      そのためには、各カードに固有のIDを付けてクリックで呼び出される関数を設定しなければなりません。
      getElementById() を使った画像の切り替えは Image Click を参照して下さい。
    3. 各カードにIDを付けてクリックで呼び出す関数を設定する card.Func() 関数です。
      id が各カードに付けるIDで、func が呼び出される関数です。
          // Click Function
          this.Func = function(num, x, y, id, func)
          {  var s3 = ' id="' + id + '" onclick=' + func + '("' + id + '")';
             var s = '<img src="' + this.img + '"' + s3 + ' style="' + this.Clip(num, x, y) + this.Position(num, x, y) + '>';
             document.write(s);
          }
      
    4. card.Func() 関数でカードを描画する方法です。
      IDにはカードを描画する順番(num)を指定します。
      "ImgClick" が呼び出される関数です。
          for(var num=0; num<48; num++)
          {   card.Func(t[num], num%12*53+10, Math.floor(num/12)*77+60, num, "ImgClick");
          }
      
    5. クリックで呼び出される ImgClick() 関数です。
      num はIDに設定したカードの順番(num)が渡されます。
      num をキーにしてリソースを取得して、カードの裏面(img/hana_ura.gif)に置き換えます。
      card.PosXY() で描画座標を取得して、style に設定して下さい。
      function ImgClick(num)
      {   document.getElementById(num).src= "img/hana_ura.gif";
          var s2 = card.PosXY(num, 10, 60, 53, 77, 12)
          document.getElementById(num).style= s2;
      }
      
    6. 描画座標を計算する card.PosXY() 関数です。
      xp, yp が描画を開始する左上座標です。
      xs, ys がカードを並べる高さと幅です。
      xn が一行に並べるカードの枚数です。
          this.PosXY = function(num, xp, yp, xs, ys, xn)
          {  var s = 'position:absolute;left:' + (((num%xn)*xs)+xp) + 'px;top:' + ((Math.floor(num/xn)*ys)+yp) + 'px;"';
             return s;
          }
      
  7. ペアーのカードを取り除く。(card_pair.html)
    ペアーのカードを取り除きます
    1. 神経衰弱ゲームのように、ペアーのカードを取り除きます。
      <script type="text/javascript">
      function ImgClick(num)
      {   document.getElementById(num).src= "img/hanafuda.gif";
          var cad= t[num];
          if (num==n1 || th[cad]>=47) return;
          if (c1==-1 || c2==-1)
          {   if (c1==-1)
              {   c1 = cad;
                  n1 = num;
              }
              else
              {   c2 = cad;
                  n2 = num;
              }
              var s1 = card.Clip(cad, cad%12*53+10, Math.floor(cad/12)*77+60);
              var s2 = card.Position(cad, num%12*53+10, Math.floor(num/12)*77+60);
              var s = s1 + ' ' + s2;
              document.getElementById(num).style= s;
              return;
          }
          // Pair 判定
          if (th[c1]!=th[c2])
          {   s1 = card.Clip(48, n1%12*53+10, Math.floor(n1/12)*77+60);
              s2 = card.Position(48, n1%12*53+10, Math.floor(n1/12)*77+60);
              s = s1 + ' ' + s2;
              document.getElementById(n1).style= s;
              s1 = card.Clip(48, n2%12*53+10, Math.floor(n2/12)*77+60);
              s2 = card.Position(48, n2%12*53+10, Math.floor(n2/12)*77+60);
              s = s1 + ' ' + s2;
              document.getElementById(n2).style= s;
              c1 = -1;
              c2 = -1;
              n1 = -1;
              n2 = -1;
              return;
          }
          // Pair の札を削除
          s1 = card.Clip(49, n1%12*53+10, Math.floor(n1/12)*77+60);
          s2 = card.Position(49, n1%12*53+10, Math.floor(n1/12)*77+60);
          s = s1 + ' ' + s2;
          document.getElementById(n1).style= s;
          s1 = card.Clip(49, n2%12*53+10, Math.floor(n2/12)*77+60);
          s2 = card.Position(49, n2%12*53+10, Math.floor(n2/12)*77+60);
          s = s1 + ' ' + s2;
          document.getElementById(n2).style= s;
          th[c1] = 49;
          th[c2] = 49;
          c1 = -1;
          c2 = -1;
          n1 = -1;
          n2 = -1;
      }
          th = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
                 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
                 11, 11, 11, 11, 12, 12, 12, 12, 0, 0 ];
          c1 = -1;
          c2 = -1;
          n1 = -1;
          n2 = -1;   
          card= new CARD("img/hanafuda.gif", 51, 75, 510, 375);
          var num;
          t = Array();
          t = card.Suflle(48);
          for(num=0; num<48; num++)
          {   card.Func(48, num%12*53+10, Math.floor(num/12)*77+60, num, "ImgClick");
          }
      </script>
      
    2. ペアーのカードを取り除くには、花札の種類(1月~12月)の情報が必要です。
      花札の画像の並びに対応して、花札の種類を定義します。
          th = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
                 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
                 11, 11, 11, 11, 12, 12, 12, 12, 0, 0 ];
      
    3. 花札の種類を設定するテーブル(th)に、取り除いたカードを記録するフラグ(49)を設定します。
          th[c1] = 49;
          th[c2] = 49;
      
    4. 一度目のクリックでめくられたカードを c1 に保存します。
      二度目のクリックでめくられたカードを c2 に保存します。
    5. 三度目のクリックで c1 と c2 を比べます。
      c1!=c2 のとき、c1,c2 のカードを裏返します。
          if (th[c1]!=th[c2])
          {   s1 = card.Clip(48, n1%12*53+10, Math.floor(n1/12)*77+60);
              s2 = card.Position(48, n1%12*53+10, Math.floor(n1/12)*77+60);
              s = s1 + ' ' + s2;
              document.getElementById(n1).style= s;
              s1 = card.Clip(48, n2%12*53+10, Math.floor(n2/12)*77+60);
              s2 = card.Position(48, n2%12*53+10, Math.floor(n2/12)*77+60);
              s = s1 + ' ' + s2;
              document.getElementById(n2).style= s;
      
    6. c1==c2 のとき、c1,c2 のカードを取り除きます。
      hanafuda.gif の最後のカードに透明色を設定して、49 を描画しても見えなくするのが良いでしょう。
          s1 = card.Clip(49, n1%12*53+10, Math.floor(n1/12)*77+60);
          s2 = card.Position(49, n1%12*53+10, Math.floor(n1/12)*77+60);
          s = s1 + ' ' + s2;
          document.getElementById(n1).style= s;
          s1 = card.Clip(49, n2%12*53+10, Math.floor(n2/12)*77+60);
          s2 = card.Position(49, n2%12*53+10, Math.floor(n2/12)*77+60);
          s = s1 + ' ' + s2;
          document.getElementById(n2).style= s;
          th[c1] = 49;
          th[c2] = 49;
      

前田稔の超初心者のプログラム入門
超初心者のプログラム入門(JavaScript)