View Single Post
Old 03-09-2013, 06:39 AM   #5
twobob
( ͡° ͜ʖ ͡°){ʇlnɐɟ ƃǝs}Týr
twobob ought to be getting tired of karma fortunes by now.twobob ought to be getting tired of karma fortunes by now.twobob ought to be getting tired of karma fortunes by now.twobob ought to be getting tired of karma fortunes by now.twobob ought to be getting tired of karma fortunes by now.twobob ought to be getting tired of karma fortunes by now.twobob ought to be getting tired of karma fortunes by now.twobob ought to be getting tired of karma fortunes by now.twobob ought to be getting tired of karma fortunes by now.twobob ought to be getting tired of karma fortunes by now.twobob ought to be getting tired of karma fortunes by now.
 
twobob's Avatar
 
Posts: 6,586
Karma: 6299993
Join Date: Jun 2012
Location: uti gratia usura (Yao ying da ying; Mo ying da yieng)
Device: PW-WIFI|K5-3G+WIFI| K4|K3-3G|DXG|K2| Rooted Nook Touch
Okay Ill repost it myself then

Code:
<html>
  <head>
    <title>Senet Game</title>
    <meta charset="UTF-8" />

<script type="text/javascript">


var xSize;
var ySize;
var xOffset = 10;
var yOffset = 10;
var gameState = "";
var currentDice = -1;
var myCanvas;
var userTurn = false;
var longGame = false;

var coefficients = [0.0625, 0.25, 6/16, 0.25, 0.0625];
var imageAnch = new Image();
var imageBaw = new Image();
var imageMw = new Image();
var imageRa = new Image();
var imageUdjat = new Image();
var imageZp = new Image();
imageAnch.src = "anchs.png";
imageBaw.src = "baws.png";
imageMw.src = "mws.png";
imageRa.src = "ras.png";
imageUdjat.src = "udjats.png";
imageZp.src = "zps.png";

function init(){
  clock();
  setInterval(clock,1000);
}


function throwDice()
{
  var result = 0;
  for( var i=0; i<4; i++)
    result += Math.round(Math.random());

  return result;
}


function moveState(state, index, dice)
{
  var newIndex;
  var newState = state;

  //console.log("moveState: " + state + " index " + index +"  dice " + dice);
  
  switch (dice) {
    case 0: newIndex = index + 6; break; // and you get another throw
    case 1: newIndex = index + 1; break;
    case 2: newIndex = index + 2; break;
    case 3: newIndex = index + 3; break;
    case 4: newIndex = index + 4; break; // and you get another throw
  }

 // console.log("newIndex: " + newIndex);
  
  // On the last fields you need to have the right throw
  if( index>26 ) {
    if( index === 29 || newIndex === 30 ) {
      newState=newState.substr(0, index)+"0"+newState.substr(index+1);
      return newState;
    } else {
      return "";
    }
  }

  if (newIndex > 29)
    return "";

  if (newIndex === 26 ) // bad field
    newIndex = 14;
  
 // console.log("A");
  
  if(state[index]==state[newIndex])  // can't move onto yourself
    return "";

  if (newIndex < index) {  // actually newIndex == 14 here
    var savedField = state[newIndex];
    newState=newState.substr(0, newIndex) + state[index]+newState.substr(newIndex+1);
    newState=newState.substr(0, index) + savedField + newState.substr(index+1);
    return newState;
  }

  
  // console.log("B");
  
  // moving over group of 3?
  var count = 0;
  for (var i = index; i < newIndex; i++) {
    if( (state[i] === state[index]) || (state[i] === "0")) {
      count = 0;
      continue;
    }
    count++
    if (count === 3)
      return "";
  }
    
  //  console.log("C");
  
  if(state[newIndex] === "0") { // moving onto empty field
    newState=newState.substr(0, newIndex) + state[index] + newState.substr(newIndex+1);
    newState=newState.substr(0, index)+"0"+newState.substr(index+1);
    
 //   console.log("New state:" + newState);
    
    return newState;
  }

  // moving onto enemy field

  if(state[newIndex] === state[newIndex-1] 
     || state[newIndex] === state[newIndex+1]) // two adjacent enemy pieces
    return "";

  //console.log("D");
    
  if(newIndex > 26 || newIndex === 25) // enemy on safe field?
    return "";

  var savedField = state[newIndex];
  newState=newState.substr(0, newIndex) + state[index] + newState.substr(newIndex+1);
  newState=newState.substr(0, index) + savedField + newState.substr(index+1);

  // console.log("New state:" + newState);

  return newState;
}


function generateMoves(who, state, dice)
{
  var moves=new Array();
  for (var i = 0; i < state.length; i++) {
    if ( state[i] == "0" )
      continue;
    if(state[i] === who) {
      var newState = moveState(state, i, dice);
      if (newState !== "")
	moves.push(newState);
    }

  }
  return moves;
}


// level is depth of total moves, who means who moves next
function evaluateStrength(state, level, who) 
{
  var countWhite = 0;
  var countBlack = 0;
  var strength = 0;
  var pFactor = 1;

  // console.log("Evaluating state Level: " + level + " state: " + state + "  who: "+ who);
  if (level > 0) {
    var averageStrength =0;
    for (var d = 0; d < 5; d++ ) {
      var bestStrength = (who === "1")? 1000 : -1000;
      var possibleMoves = generateMoves(who, state, d);
      if( possibleMoves.length < 1){  // No moves possible, other's turn
	possibleMoves.push(state);
      }
      for (var i=0; i< possibleMoves.length; i++) {
        var whoNext = (who === "1") ? "2" : "1";
        if( d===0 || d=== 4)
	  whoNext = who;
	strength = evaluateStrength(possibleMoves[i], level - 1, whoNext);
        if (who === "1") {
	  bestStrength = (strength < bestStrength)? strength:bestStrength;
	} else {
	  bestStrength = (strength < bestStrength)? bestStrength:strength;
	}
        // console.log("Level: " + level + "  dice: " +  d + " state: " + possibleMoves[i] + "  strength: "+ strength + "  bestStrength " + bestStrength);
      }
      averageStrength += bestStrength * coefficients[d];
    }
    // console.log("Returning Level: " + level + " state: " + state + "  strength: "+ averageStrength);
    return averageStrength;
  }

  for (var i = 0; i < state.length; i++) {
    if ( state[i] == "0" )
      continue;
    if(state[i] === "2") { // White
      strength += i;
      if (i == 25 || i==27 || i== 28) // safe fields
         strength += Math.pow(i, pFactor);
      countWhite++;

      if (i < (state.length-1) && state[i+1] === "2" ) {
        strength += Math.pow(i, pFactor) * (countBlack/2 +1);
        i++;
        countWhite++;

        if (i < (state.length-1) && state[i+1] === "2" ) {
          strength += Math.pow(i, pFactor) * (countBlack/2 +1);
          i++;
          countWhite++;
        }
        continue;
      }
    } else {  // Black
      strength -= i;
      if (i == 25 || i==27 || i== 28) // safe fields
         strength -= Math.pow(i, pFactor);
      countBlack++;

      if (i < (state.length-1) && state[i+1] === "1" ) {
        strength -= Math.pow(i, pFactor) * (countWhite/2 +1);
        i++;
        countBlack++;

        if (i < (state.length-1) && state[i+1] === "1" ) {
          strength += Math.pow(i, pFactor) * (countWhite/2 +1);
          i++;
          countBlack++;
        }
        continue;
      }

    }
  }
  if(longGame) {
    strength += (7-countWhite) * 100;
    strength -= (7-countBlack) * 100;
  } else {
    strength += (5-countWhite) * 100;
    strength -= (5-countBlack) * 100;
  }
  // console.log("State " + state + ": " +strength);
  return strength;
}


function prepareHumanTurn()
{
  userTurn = false;
  updateStrength();

  currentDice = throwDice();
  // document.getElementById("dice").innerHTML="Dice: " + currentDice;
  showDice(currentDice);
  document.getElementById("notice").innerHTML="Your turn!";
  var possibleMoves = generateMoves("2", gameState, currentDice);
  for (var i = 0; i < possibleMoves.length; i++)
    console.log("Your moves: " + possibleMoves[i]);
  if( possibleMoves.length < 1 ) {
    document.getElementById("notice").innerHTML="You cannot move!";
    setTimeout(prepareComputerTurn, 3000) //wait two seconds before continuing
    return;
  }
  userTurn=true;
}


function showDice(d)
{
  switch (d) {
  case 0:
    document.getElementById("dImage").src="dice0_320.png";
    document.getElementById("dImage").alt="0";
    break;
  case 1:
    document.getElementById("dImage").src="dice1_320.png";
    document.getElementById("dImage").alt="1";
    break;
  case 2:
    document.getElementById("dImage").src="dice2_320.png";
    document.getElementById("dImage").alt="2";
    break;
  case 3:
    document.getElementById("dImage").src="dice3_320.png";
    document.getElementById("dImage").alt="3";
    break;
  case 4:
    document.getElementById("dImage").src="dice4_320.png";
    document.getElementById("dImage").alt="4";
    break;
    }
}


function getComputerStrength()
{
  var radios = document.getElementsByName('cstrength');

  for (var i = 0, length = radios.length; i < length; i++) {
    if (radios[i].checked) {
//        alert(radios[i].value);
      return radios[i].value;
    }
  }
  return 1;
}


function prepareComputerTurn()
{
  userTurn = false;
  currentDice = throwDice();
  // document.getElementById("dice").innerHTML="Dice: " + currentDice;
  showDice(currentDice);
  console.log("My move: " + currentDice + "-------------------------------------");
  document.getElementById("notice").innerHTML="My move...";
  setTimeout(computerTurn, 2000) //wait five seconds before continuing
}

function computerTurn()
{  
  document.getElementById("notice").innerHTML="Thinking...";
  var possibleMoves = generateMoves("1", gameState, currentDice);
  if( possibleMoves.length < 1 ) {
    document.getElementById("notice").innerHTML="Cannot move!";
    setTimeout(prepareHumanTurn, 3000) //wait five seconds before continuing
  }

  var minStrength=1000;
  var minIndex =-1;
  for(var i = 0; i<possibleMoves.length; i++){
    var strength;
    if( currentDice === 0 || currentDice === 4 )
      strength = evaluateStrength(possibleMoves[i], getComputerStrength(), "1");
    else
      strength = evaluateStrength(possibleMoves[i], getComputerStrength(), "2");
   // console.log("State: " + possibleMoves[i] + ": " + strength);
    if(strength < minStrength){
      minStrength = strength;
      minIndex = i;
    }
  }
  
  console.log("Choosing: " + possibleMoves[minIndex]);
  drawUpdatedState(myCanvas.getContext('2d'), gameState, possibleMoves[minIndex]);

  gameState = possibleMoves[minIndex];
 
  if(gameState.indexOf("1") === -1){
    document.getElementById("notice").innerHTML="I won!";
    userTurn = false;
    return;
  }
  
  if (currentDice === 0 || currentDice === 4) {
    console.log("Computer gets another turn");
    prepareComputerTurn();
    return;
  }

  prepareHumanTurn(); 
}


function drawPin(ctx, state, i)
{
  // console.log("Drawing pin number " +i);
  ctx.strokeStyle="black";
  ctx.lineWidth=3;
  ctx.beginPath();
  if (state[i]=="2" )
    ctx.fillStyle = "white";
  else
    ctx.fillStyle = "black";
  if (Math.floor(i/10) !== 1){
    ctx.arc(xOffset + xSize/6 + (xSize/3) * Math.floor(i/10), yOffset + ySize - (ySize /20 + (ySize/10) * (i%10)),
            xSize/20, 0, 2*Math.PI, true);
  } else {
    ctx.arc(xOffset + xSize/6 + (xSize/3) * Math.floor(i/10), yOffset + ySize /20 + (ySize/10) * (i%10),
            xSize/20, 0, 2*Math.PI, true);
  }
  ctx.closePath();
  ctx.stroke();
  ctx.fill();
}

function drawState(ctx, state) {
  if ( state.length != 30 )
    alert("Illegal State: " + state);
  ctx.save();
  console.log("DRAWING STATE " + state);
  ctx.strokeStyle="black";
  ctx.lineWidth=3;
  for (var i = 0; i < state.length; i++) {
    if ( state[i] == "0" )
      continue;
    drawPin(ctx, state, i);
  }
  ctx.restore();
}

function drawPictureField(ctx, i) 
{
  if (i === 14)
    ctx.drawImage(imageAnch, xOffset + 1 + (xSize/3) * Math.floor(15/10), yOffset +1 + ySize-((ySize/10) * ((15%10)+1)), xSize/3.2, ySize/10.2-2);
  if (i === 25)
    ctx.drawImage(imageUdjat, xOffset + 1 + (xSize/3) * Math.floor(25/10), yOffset +1 + ySize-((ySize/10) * ((25%10)+1)), xSize/3.2, ySize/10.2-2);
  if (i === 26)
    ctx.drawImage(imageMw, xOffset + 1 + (xSize/3) * Math.floor(26/10), yOffset +1 + ySize-((ySize/10) * ((26%10)+1)), xSize/3.2, ySize/10.2-2);
  if (i === 27)
    ctx.drawImage(imageBaw, xOffset + 1 + (xSize/3) * Math.floor(27/10), yOffset +1 + ySize-((ySize/10) * ((27%10)+1)), xSize/3.2, ySize/10.2-2);
  if (i === 28)
    ctx.drawImage(imageZp, xOffset + 1 + (xSize/3) * Math.floor(28/10), yOffset +1 + ySize-((ySize/10) * ((28%10)+1)), xSize/3.2, ySize/10.2-2);
  if (i === 29)
    ctx.drawImage(imageRa, xOffset + 1 + (xSize/3) * Math.floor(29/10), yOffset +1 + ySize-((ySize/10) * ((29%10)+1)), xSize/3.2, ySize/10.2-2);
}


function drawUpdatedState(ctx, oldState, newState) {
  console.log("Updating state to " + newState);
  if ( oldState.length != 30 || newState.length != 30)
    alert("Illegal State: " + state);
  
  ctx.save();
  for (var i = 0; i < oldState.length; i++) {
    ctx.fillStyle = "white";
    if ( oldState[i] === newState[i] )
      continue;

    //console.log("Doing something for " + i);
      
    if (Math.floor(i/10) !== 1) {
      ctx.fillRect(xOffset + 1 + (xSize/3) * Math.floor(i/10), yOffset +1 + ySize-((ySize/10) * ((i%10)+1)), xSize/3.1, ySize/10 - 4);
    }  else {   
      ctx.fillRect(xOffset + 1 + (xSize/3) * Math.floor(i/10), yOffset +1+ (ySize/10) * (i%10), xSize/3.1, ySize/10 - 4);
    }
  
    drawPictureField(ctx, i);
    
    if ( newState[i] === "0")
      continue;
 
    drawPin(ctx, newState, i);

  }
  ctx.restore();
}


function mouseClick(e)
{
  console.log("Position: " + e.pageX + ", " + e.pageY);
  var rect = myCanvas.getBoundingClientRect();
  var fieldX = Math.floor((e.pageX-xOffset-rect.left)/(xSize / 3));
  var fieldY = Math.floor((e.pageY-yOffset-rect.top)/(ySize / 10));
  var field;
  if (fieldX !== 1)
    field = fieldX *10 + (9-fieldY);
  else
    field = fieldX *10 + fieldY;
  console.log("Field: " + fieldX + ", " + fieldY +"  :" +field);

  if( !userTurn ) return;

  if( gameState[field] != "2" ) {
    document.getElementById("notice").innerHTML="Not your piece!";
    return;
  }
  
  var newState = moveState(gameState, field, currentDice);
  if (newState === "") {
    document.getElementById("notice").innerHTML="Illegal Move!";
    return;
  }
  
  drawUpdatedState(myCanvas.getContext('2d'), gameState, newState);
 
  gameState = newState;
  
  if(gameState.indexOf("2") === -1){
    document.getElementById("notice").innerHTML="You won!";
    userTurn = false;
    return;
  }
  
  if ( currentDice == 0 || currentDice == 4){ // Move twice
    prepareHumanTurn();
    return;
  }
  
  prepareComputerTurn();
}


function drawBoard() {
  console.log("Drawing board, game state:" + gameState);
  myCanvas = document.getElementById('canvas');
  
 // myCanvas.setAttribute("width",window.innerWidth*1/3);
 // myCanvas.setAttribute("heights", window.innerHeight-4); 
  var ctx = myCanvas.getContext('2d');
  xSize = myCanvas.width-(2*xOffset);
  ySize = myCanvas.height - (2 * yOffset);
  ctx.save();
  ctx.beginPath();
  ctx.clearRect ( xOffset,yOffset,xSize, ySize);
  
  ctx.strokeStyle="black";
  ctx.strokeRect(xOffset,yOffset,xSize, ySize);
  ctx.restore();
  
  ctx.strokeStyle = "black";
  // draw horizontal lines
  for( var i = 1; i < 10; i++) {
    ctx.moveTo(xOffset, yOffset + (ySize /10) * i);
    ctx.lineTo(xOffset + xSize, yOffset + (ySize/10) * i);
  }

  // draw vertical lines
  for (var i = 1; i < 3; i++) {
    ctx.moveTo(xOffset + (xSize/3)*i, yOffset);
    ctx.lineTo(xOffset + (xSize/3)*i, yOffset + ySize);
  }
  ctx.stroke();
  
  ctx.restore();
//   drawUpdatedState(ctx, "000000000000000000000000000000", gameState);
  for (var i = 0; i< 30; i++)
    drawPictureField(ctx, i);
  
  drawState(ctx, gameState);

}

function updateStrength()
{
  var strength = evaluateStrength(gameState, 0);
  document.getElementById("score").innerHTML="Your strength: " + strength;  
}


function showRules()
{

  var v = document.getElementById("rules").getAttribute("hidden");
  console.log("Attr: " + v);
  if(v === null) {
    document.getElementById("rules").setAttribute("hidden", true);
    document.getElementById("rules_button").innerHTML="Show Rules";
  } else {
    document.getElementById("rules").removeAttribute("hidden");
    document.getElementById("rules_button").innerHTML="Hide Rules";
  }
}



function newGame()
{
  longGame = false;
  gameState = "121212121200000000000000000000";
  if(document.getElementById('duration_long').checked) {
    longGame = true;
    gameState = "121212121212120000000000000000";
  }
  drawBoard();
  
  myCanvas.addEventListener('click', mouseClick, false);
  prepareHumanTurn();
}

    </script>
    <style type="text/css">
      #canvas { border: 1px solid black;}
      #board {
         float:left;
         top:10;
         width:400px;
         right:20;
         padding:10;
      }
      #controls {
        width:260px;
        top:10;
        padding:10px;
        float:left;
        text-align:center;
      }
    </style>

  </head>
  <body onload="newGame();">
    <div id="title" text-align="center" width="600px"> <h1>Game of Senet</h1></div>
    <div id="board">
      <canvas id="canvas" height=720 width=400></canvas>
    </div>
    <div id="controls">
      <button onClick="newGame();">New Game</button>
      <p>Duration:<br>
	<input id="duration_short" type="radio" name="duration" value="short" checked> short
	<input id="duration_long" type="radio" name="duration" value="long"> long
      </p>
      <p> Computer strength: <br>
	<input id="strength_1" type="radio" name="cstrength" value="1" checked>  1
	<input id="strength_2" type="radio" name="cstrength" value="2"> 2
	<input id="strength_3" type="radio" name="cstrength" value="3"> 3
	<br>
	<hr />
	<br>
      <p id="score"> Your strength: 0</p>
      <p id="notice">Your Turn!</p> 
      <p id="dice"> <img id="dImage" width="240" height="240" border="0" alt="Dice 0" src="dice0_320.png" title="Dice"/></p> 
    </div>
    <div id="footer" style="clear:both;text-align:center;">
      <button id="rules_button" onClick="showRules();">Show Rules</button>
      <div id="rules" style="text-align:left;" hidden>
        <p> Each player has 5 (short game) or 7 pawns (long game) on a board with 30 fields. Pieces move forward in a snake-like line.
  The goal is to get all your pieces off the board at the right upper corner.</p>
  <img id="dImage" width="200" height="320" border="0" alt="Dice 0" src="board.png" title="Dice"/>
  <p> To the lower right the result of throwing the "dice" is shown. The "dice" is actually 4 flattened sticks which have a black and
  a white side. Throwing 1, 2 or 3 black sticks means the player can mode one piece the same number of fields forward. If no black side faces upwards,
  the player can move 6 fields, and 4 if all black sides are up. In both cases, the player may cast the sticks a second time. Note that it is far
  more likely to throw a 2 than a 6.</p>
  <p>Pieces must be moved forward. A piece can only be moved onto an empty field or a field with an enemy piece on it, in which case the enemy piece
  and your piece change positions. You cannot move onto a field already occupied by your own pawns. Groups of 2 pawns are safe and you cannot move onto
  their fields. Groups of three enemy pieces can additionally not be passed.<p>
  <p>There are 6 special fields on the board. The three last fields and the one showing the Udjat-Eye are safe fields, where enemy pieces cannot be
  pushed away from. The water field makes a piece fall back to the Anch field. For a piece to leave the board it needs to reach exactly the field after
  the end of the board. Pieces on the three last fields can only be moved off the board, i.e. a pawn e.g. on the field with the three birds can only be
  moved if a 3 is thrown.</p>
  <p>The difficulty, that is the strength of the computer, can be changed during game-play. The strength gives the number of moves the computer looks
  ahead and then chooses the likeliest best move for himself.</p>
      </div>
    </div>
  </body>
</html>

Last edited by twobob; 03-09-2013 at 11:44 AM.
twobob is offline   Reply With Quote