Newer
Older
Import / web / puzzlrz.com / sudoku / sudoku.js
/*
    Sudoku Puzzle Javascript Frontend 
    (C) Copyright 2007
    John Ryland <jryland@invertedlogic.com>

    ALL RIGHTS RESERVED
*/
var pauseBackup = new Array;
var puzzle = new Array;
var solution = new Array;
var seed = null;
var checkCount = 0;
var startTime = 0;
var startPausedTime = 0;
var pausedTime = 0;
var updateTimerId = 0;
var lastItem = 0;
var lastVal = 0;

var facebookEnabled = false;
var paused = false;
var solved = false;
var menuShown = false;

function pageClick()
{
  if (menuShown) {
    document.getElementById("menuWrapper").style.display = "none";
    setTimeout("menuShown = false;", 10);
  }
}

function scaleToFit()
{
  var dim = getViewportSize();
  var ratio = dim.h / dim.w;
  var s = dim.h / 1400;
  if ( ratio > 1.4 )
    s = dim.w / 970;

  var scale = 'scale(' + s + ')';
  if (ratio > 1.1) {
    off = 0;//(50*s) / ratio;
  } else {
    off = 50;
  }

  scaleElement(document.getElementById("content"), scale, off);
  forEach(document, '.dialogFrame', function(elem){ scaleElement(elem.children[0], scale, off); });
}

window.onorientationchange = function(event)
{
  scaleToFit();
}

function totalTime()
{
  return Math.round((currentMilliSecond() - startTime - pausedTime) / 1000);
}

function setIconButtonEnabled(buttonId, state)
{
  var button = document.getElementById(buttonId);
  if (button) {
    button.className = "IconButton";
    if (!state)
      button.className += " disabledIconButton";
  }
}

function setMenuItemEnabled(buttonId, state)
{
  var button = document.getElementById(buttonId);
  if (button) {
    button.className = "MenuItem";
    if (!state)
      button.className += " disabledMenuItem";
  }
}

function isButtonEnabled(buttonId)
{
  var button = document.getElementById(buttonId);
  if (button && hasClass(button, "disabledButton"))
    return false;
  return true;
}

function enableAllNumButtons()
{
  for (var i = 1; i <= 9; i++) {
    var button = document.getElementById('Button_' + i.toString());
    button.className = "numButton";
/*
      // Modify colors via JavaScript
      var stopsList = button.querySelectorAll('#stop7394');
      if (stopsList && stopsList.length > 0)
        for (var i = 0; i < stopsList.length; i += 1)
              stopsList[i].style = "stop-color:#e1f1e1;stop-opacity:1;";
      stopsList = button.querySelectorAll('#stop7396');
      if (stopsList && stopsList.length > 0)
        for (var i = 0; i < stopsList.length; i += 1)
              stopsList[i].style = "stop-color:#e1f1e1;stop-opacity:0;";
      stopsList = button.querySelectorAll('#stop7402');
      if (stopsList && stopsList.length > 0)
        for (var i = 0; i < stopsList.length; i += 1)
              stopsList[i].style = "stop-color:#c0d0c0;stop-opacity:1;";
      stopsList = button.querySelectorAll('#stop7404');
      if (stopsList && stopsList.length > 0)
        for (var i = 0; i < stopsList.length; i += 1)
  	      stopsList[i].style = "stop-color:#979797;stop-opacity:1;";
*/
  }
}

function init()
{
  if (facebookEnabled) {
    FB.api("/me/achievements", "GET", {}, function (response) {
      if (response && !response.error) {
	alert('achievements response :' + response);
      }
    });
  }
  // newGame();
  // loadGame(getUrlArgs()["type"], getUrlArgs()["difficulty"], getUrlArgs()["id"]);
}

//    var json = JSON.stringify(eval("(" + data + ")"));

function loadGame(type, difficulty, id)
{
  document.getElementById("newDialogWrapper").style.display = "none";
  document.getElementById("inProgress").style.display = "block";

  var url = "bin/sudoku.php?time=" + (new Date()).getTime() + "&";
  if (type)       url += "type=" + type + "&";
  if (difficulty) url += "difficulty=" + difficulty + "&";
  if (id)         url += "id=" + id + "&";
  // TODO: remove any trailing &

  loadJsonAsync(url, function(okay, json) {
    if (okay)
      initBoard(json);
    else
      alert("There was a problem getting a new game. Try again later.")
  });
}

function loadDailyChallenge()
{
    //var d = new Date();
    //d.setHours(0,0,0,0,0);
    //d.getTime());
    loadGame('daily');
}

function logEvent(str)
{
    if (facebookEnabled)
      FB.AppEvents.logEvent(str);
}

function newGame()
{
    logEvent("newGame");
    //loadGame('', 'hard', '');
    
    //document.getElementById("newDialogWrapper").style.display = "block";
    document.getElementById("newDialogWrapper").style.display = "block";
    //setTimeout('document.getElementById("newDialogWrapper").style.display = "none"', 2000);

    document.getElementById("newDialogCancel").style.display = "block";

}

function initBoard(json)
{
    document.getElementById("inProgress").style.display = "none";
    enableAllNumButtons();

  //  var lines = initRequest.responseText.split("\n");
    seed = json.id;//lines[0];
    var clue = json.clues;
    var puz = json.solution;
    for (var i = 1; i < 82; i++) {
        document.getElementById(i.toString()).className = "EmptyNumber";
        solution[i] = puz.charAt(i-1).toUpperCase();
        puzzle[i] = clue.charAt(i-1).toUpperCase();
        if ( puzzle[i] != '0' ) {
            document.getElementById(i.toString()).innerHTML = puzzle[i];
            // document.getElementById(i.toString()).readOnly = true;
            document.getElementById(i.toString()).className = "ClueNumber";
        }
    }
    resetGame();
    lastItem = 41;
    lastVal = 0;
    highlightItems(41);
}

function trophies()
{
      document.getElementById("leaderBoardWrapper").style.display = "block";
}

function menu()
{
    if (!menuShown) {
      document.getElementById("menuWrapper").style.display = "block";
      setTimeout("menuShown = true;", 10);
    }
}

document.addEventListener("visibilitychange", function() {
  console.log(document.hidden, document.visibilityState);
  doPause();
}, false);

function solveGame()
{
    logEvent("solveGame");
    if ( !confirm("Do you really want to see the answers?\n" +
                "You will not be allowed to continue after this.\n" +
                "Are you sure you want to proceed?") )
        return;
    for (var i = 1; i < 82; i++) {
        document.getElementById(i.toString()).innerHTML = solution[i];

//        
//        puzzle[i] = solution[i];

        //document.getElementById(i.toString()).readOnly = true;
    }

    setMenuItemEnabled("resetButton", false);
    setIconButtonEnabled("pauseButton", false);
    setIconButtonEnabled("resumeButton", false);
    setMenuItemEnabled("printButton", false);
    setMenuItemEnabled("checkButton", false);
    setMenuItemEnabled("solveButton", false);
//    document.getElementById("status").innerHTML = "TOO HARD?" +
//        "<br>Answers checked " + checkCount + " time(s)." +
//        "<br>Gave up after " + totalTime() + " seconds";
    clearTimeout( updateTimerId );
    updateTimerId = 0;
    //document.getElementById("time").innerHTML = "";

    highlightItems(0);
}

function doResume()
{
        paused = false;
        clearTimeout( updateTimerId );
        updateTimerId = setTimeout( "updateTime()", 10 );
        pausedTime += currentMilliSecond() - startPausedTime;
        startPausedTime = 0;
        //document.getElementById("pauseButton").value = "Pause";
        document.getElementById("pauseButton").style.display = "block";
        document.getElementById("resumeButton").style.display = "none";
        for (var i = 1; i < 82; i++) {
            document.getElementById(i.toString()).innerHTML = pauseBackup[i];
            if ( puzzle[i] == '0' ) {
                document.getElementById(i.toString()).className = "EnteredNumber";
            } else {
                document.getElementById(i.toString()).className = "ClueNumber";
            }
        }
        setIconButtonEnabled("newButton", true);
        setMenuItemEnabled("resetButton", true);
        setMenuItemEnabled("printButton", true);
        setMenuItemEnabled("checkButton", true);
        setMenuItemEnabled("solveButton", true);
        //document.getElementById("status").innerHTML = "";
}

function doPause()
{
        if ( startPausedTime )
	   return;
        clearTimeout( updateTimerId );
        updateTimerId = 0;
        paused = true;
        startPausedTime = currentMilliSecond();
        //document.getElementById("pauseButton").value = "Resume";
        document.getElementById("pauseButton").style.display = "none";
        document.getElementById("resumeButton").style.display = "block";
        for (var i = 1; i < 82; i++) {
            pauseBackup[i] = document.getElementById(i.toString()).innerHTML;
            document.getElementById(i.toString()).innerHTML = "";
            document.getElementById(i.toString()).className = "PausedNumber";
        }
        document.getElementById("39").innerHTML = "P";
        document.getElementById("40").innerHTML = "A";
        document.getElementById("41").innerHTML = "U";
        document.getElementById("42").innerHTML = "S";
        document.getElementById("43").innerHTML = "E";
        setIconButtonEnabled("newButton", false);
        setMenuItemEnabled("resetButton", false);
        setMenuItemEnabled("printButton", false);
        setMenuItemEnabled("checkButton", false);
        setMenuItemEnabled("solveButton", false);
        //document.getElementById("status").innerHTML = "PAUSED - Click resume to continue";
}

function pauseGame()
{
    if ( solved )
       return;

//    alert("Pause not implemented");
    if ( startPausedTime ) {
        doResume();
    } else {
        doPause();
    }
}

function resetGame()
{
    logEvent("resetGame");
    startTime = currentMilliSecond();
    paused = false;
    pausedTime = 0;
    checkCount = 0;
    lastItem = 0;
    lastVal = 0;
    clearTimeout( updateTimerId );
    updateTimerId = setTimeout( "updateTime()", 10 );
    solved = false;
    for (var i = 1; i < 82; i++) {
        if ( puzzle[i] == '0' ) {
            document.getElementById(i.toString()).innerHTML = "";
            // document.getElementById(i.toString()).readOnly = false;
            document.getElementById(i.toString()).className = "EnteredNumber";
        }
    }
    setMenuItemEnabled("resetButton", true);
    setIconButtonEnabled("pauseButton", true);
    setIconButtonEnabled("resumeButton", true);
    setMenuItemEnabled("printButton", true);
    setMenuItemEnabled("checkButton", true);
    setMenuItemEnabled("solveButton", true);
    //document.getElementById("status").innerHTML = "";

    enableAllNumButtons();

/*
    for (var i = 1; i < 82; i++) 
    	setMenuItemEnabled(i.toString(), true);
*/

    document.onkeydown = keyEventHandler;
}

function showWinScreen()
{
    var min = Math.floor(totalTime() / 60);
    var sec = Math.floor(totalTime() % 60);
    var Sec1 = Math.floor(sec / 10);
    var Sec2 = Math.floor(sec % 10);

    //document.getElementById("status").innerHTML = "CONGRATULATIONS - PUZZLE SOLVED" +
    //    "<br>Answers checked " + checkCount + " time(s)." +
    //    "<br>Solved in " + totalTime() + " seconds";

    //alert("\nCONGRATULATIONS - PUZZLE SOLVED" + "\n\nSolved in " + min + ":" + Sec1 + Sec2 + "\n");

    document.getElementById("winScreenWrapper").style.display = "block";
    document.getElementById("winScreenContents").innerHTML = "<br>Solved in " + min + ":" + Sec1 + Sec2 + "\n";
}

function updateTime()
{
    clearTimeout( updateTimerId );
    updateTimerId = setTimeout( "updateTime()", 1000 );
    //document.getElementById("time").innerHTML = totalTime() + " seconds";
    var min = Math.floor(totalTime() / 60);
    var sec = Math.floor(totalTime() % 60);
    var Sec1 = Math.floor(sec / 10);
    var Sec2 = Math.floor(sec % 10);
    document.getElementById("Timer").innerHTML = min + ":" + Sec1 + Sec2;
}

var fromClick = false;
function highlightItems(item)
{
    if ( !isButtonEnabled("solveButton") || solved ) {
      for (var i = 1; i < 82; i++)
        if (puzzle[i] != '0')
          document.getElementById(i.toString()).className = "ClueNumber";
        else
          document.getElementById(i.toString()).className = "EnteredNumber";
      return;
    }

    var usedNumbers = new Array;
    for (var i = 1; i <= 9; i++)
        usedNumbers[i] = 0;
    for (var i = 1; i < 82; i++) {
      document.getElementById(i.toString()).className = "EmptyNumber";
      var v = document.getElementById(i.toString()).innerHTML;
      if ( v.toString() == solution[i].toString() )
        usedNumbers[v]++;
      if (puzzle[i] != '0')
        document.getElementById(i.toString()).className = "ClueNumber";
      else if (v != "" && v != " ")
        document.getElementById(i.toString()).className = "EnteredNumber";
    }

    var val = document.getElementById(item.toString()).innerHTML;
    if (!val || !fromClick)
    {
      for (var i = 1; i < 82; i++) {
        var sameCol = (((i-1) % 9) == ((item - 1) % 9));
        var sameRow = (Math.floor((i-1) / 9) == Math.floor((item - 1) / 9));
        var sameSqr = (Math.floor((i-1) / 27) == Math.floor((item - 1) / 27))
                  &&  (Math.floor(((i-1) % 9)/3) == Math.floor(((item - 1) % 9)/3));
	if ( i == item )
	    document.getElementById(i.toString()).className = "ClueNumberFocused";
	else if ( sameCol || sameRow )
	    document.getElementById(i.toString()).className = "ClueNumberFocusedLine";
	else if ( sameSqr )
	    document.getElementById(i.toString()).className = "ClueNumberFocusedSquare";
      }
    } else {
      for (var i = 1; i < 82; i++) {
	if ( val && val == document.getElementById(i.toString()).innerHTML )
	{
            // .SameValueRowCol {
            document.getElementById(i.toString()).className = "SameValue";
	    if ( i == item )
               document.getElementById(i.toString()).className = "SameValueFocused";
	}
      }
    }
    fromClick = false;
    for (var i = 1; i <= 9; i++) {
        if ( usedNumbers[i] >= 9 ) {
	    var button = document.getElementById('Button_' + i.toString());

	    if (!hasClass(button, "disabledNumButton"))
                button.className += " disabledNumButton";
/*
	    // Modify colors via JavaScript
	    var stopsList = button.querySelectorAll('#stop7394');
	    for (var i = 0; i < stopsList.length; i += 1)
		    stopsList[i].style = "stop-color:#d1d1d1;stop-opacity:1;";
	    stopsList = button.querySelectorAll('#stop7396');
	    for (var i = 0; i < stopsList.length; i += 1)
		    stopsList[i].style = "stop-color:#d1d1d1;stop-opacity:0;";
	    stopsList = button.querySelectorAll('#stop7402');
	    for (var i = 0; i < stopsList.length; i += 1)
		    stopsList[i].style = "stop-color:#b0b0b0;stop-opacity:1;";
	    stopsList = button.querySelectorAll('#stop7404');
	    for (var i = 0; i < stopsList.length; i += 1)
		    stopsList[i].style = "stop-color:#878787;stop-opacity:1;";
*/
        }
    }
    checkAnswers()
}

function checkAnswers()
{
    if ( !isButtonEnabled("solveButton") || solved )
	return;

    //alert("Check not implemented");
    var clues = 0;
    var correct = 0;
    var wrong = 0;
    var empty = 0;
    for (var i = 1; i < 82; i++) {
        if ( puzzle[i] == '0' ) {
            var val = document.getElementById(i.toString()).innerHTML;
            if ( val == "" || val == " " ) {
                //document.getElementById(i.toString()).className = "EnteredNumber";
                empty++;
            } else if ( val.toString() != solution[i].toString() ) {
                document.getElementById(i.toString()).className = "WrongNumber";
                var wrongVal = document.getElementById(i.toString()).innerHTML;
		var wrongStr = "document.getElementById(" + i.toString() + ").innerHTML";
                setTimeout("{ if (" + wrongStr + " == " + wrongVal + ") { lastVal = 0; " + wrongStr + " = ''; } }", 1250);
                wrong++;
            } else {
                document.getElementById(i.toString()).className += " CorrectNumber";
                correct++;
            }
        } else {
            clues++;
        }
    }
    checkCount++;
    //document.getElementById("status").innerHTML = "Clues: " + clues + " Correct: " + correct +
    //                                    " Wrong: " + wrong + " Empty: " + empty +
    //                                    "<br>Answers checked " + checkCount + " time(s).";
    if ( wrong == 0 && empty == 0 ) {
        setIconButtonEnabled("pauseButton", false);
        setIconButtonEnabled("resumeButton", false);
        setMenuItemEnabled("printButton", false);
        setMenuItemEnabled("checkButton", false);
        setMenuItemEnabled("solveButton", false);


/* Replace {achievement-id} with the ID of your achievement. */
        if (facebookEnabled) {
            FB.api("/me/achievements", "POST",
              { "achievement": "956642174439599" // {solved-a-sudoku}"
              }, function (response) {
                if (response && !response.error) {
                  // Verify the achievement was published.
                }
              }
            );
        }

/*
        for (var i = 1; i < 82; i++) 
    	    setMenuItemEnabled(i.toString(), false);
*/

        clearTimeout( updateTimerId );
        updateTimerId = 0;
        //document.getElementById("time").innerHTML = "";

    	solved = true;
        highlightItems(0);
        setTimeout("showWinScreen();", 10);
    }
}

function resetItem(item)
{
    if ( paused == true ) {
       doResume();
    }

    if ( !isButtonEnabled("solveButton") || solved )
	return;
    if ( lastItem != 0 && lastItem != "0" ) {
       if ( puzzle[lastItem] == '0' && lastVal ) {
       //if ( !document.getElementById(lastItem.toString()).readOnly && lastVal) {
         document.getElementById(lastItem.toString()).innerHTML = lastVal;
       }
    }
    lastItem = 0;
    lastItem = item;
    if ( puzzle[item] == '0' ) {
    // if ( !document.getElementById(item.toString()).readOnly ) {
        lastItem = item;
        lastVal = document.getElementById(item).innerHTML.toString();
        //document.getElementById(item).innerHTML = "";
    }
    //document.getElementById(item.toString()).focus();
    document.getElementById(item.toString()).blur();
    //document.getElementById("status").innerHTML = "";
    for (var i = 1; i < 82; i++) {
        if ( puzzle[i] == '0' ) {
            document.getElementById(i.toString()).className = "EnteredNumber";
        }
    }
    fromClick = true;
    highlightItems(item);
}

function enterNumber(ch)
{
    if ( lastItem != 0 && lastItem != "0" ) {
       var oldCh  = document.getElementById(lastItem.toString()).innerHTML;
       var button = document.getElementById('Button_' + ch.toString());
       // if ( !document.getElementById(lastItem.toString()).readOnly ) {
       if ( puzzle[lastItem] == '0'                       // It's an editable value 
           && ( ch > '0' && ch <= '9' )                   // A valid number
           && (oldCh.toString() != solution[lastItem].toString())  // It's not already the correct value
	   && !hasClass(button, "disabledNumButton")     // The number button is enabled
          ) {
         document.getElementById(lastItem.toString()).innerHTML = ch;
	 lastVal = ch;
       }

       if ( ( ch > '0' && ch <= '9' )                   // A valid number
           && (oldCh.toString() == solution[lastItem].toString())  // It's the correct value
          )
       {
         for (var i = 0; i < 81; i++) {
            var j = i + lastItem; // find the position of the next occurance from the current
            j = (j % 81) + 1;
            if (document.getElementById(j.toString()).innerHTML == ch) {
                lastItem = j;
	        lastVal = ch;
                break;
            }
         }
         //highlightItems(lastItem);
         resetItem(lastItem);
       } else {
         highlightItems(lastItem);
         setTimeout("resetItem(lastItem);", 500);	
       }
       //document.getElementById(lastItem.toString()).focus();
    }
}

function keyEventHandler(event)
{
    var k;
    if (window.event)
        k = window.event.keyCode;
    else if (event)
        k = event.which;

    if (!event)
        event = window.event;
    target = event.target ? event.target : event.srcElement;

    if (lastItem) { 
    //if (target) {
//        k = event.keyCode;
        ch = String.fromCharCode(k);
        // i = parseInt(target.id);
        i = lastItem;
        if ( i == NaN || i < 1 || i > 81)
            return false;
        if ( !document.getElementById(i.toString()) )
	    return false;
        //if ( puzzle[i] == '0' ) {
        // if ( !document.getElementById(i.toString()).readOnly ) {
            if ( k == 8 || ch <= ' ' )
                document.getElementById(i.toString()).innerHTML = "";
            if ( ch > '0' && ch <= '9' ) {
                //document.getElementById(i.toString()).innerHTML = "";
                //document.getElementById(i.toString()).innerHTML= ch;
                lastItem = i;
		enterNumber(ch);
		return;
                //lastVal = ch;
                //highlightItems(i);
            }
        //}
        if ( k < 37 || k > 40 )
            return false;
        if ((k == 37 && (i % 9) == 1) || (k == 38 && (i - 9) <= 0))
            return false;
        if ((k == 39 && (i % 9) == 0) || (k == 40 && (i + 9) > 81))
            return false;
        if ( puzzle[i] == '0' )
            document.getElementById(i.toString()).className = "EnteredNumber";
        else
            document.getElementById(i.toString()).className = "ClueNumber";

        var prevI = i;
        if ( k == 37 ) i--;       // Left
        if ( k == 38 ) i -= 9;    // Up
        if ( k == 39 ) i++;       // Right
        if ( k == 40 ) i += 9;    // Down
        //document.getElementById(i.toString()).focus();
        if (i != prevI)
           fromClick = true;

    if ( lastItem != 0 && lastItem != "0" ) {
       if ( puzzle[lastItem] == '0' && lastVal ) {
       // if ( !document.getElementById(lastItem.toString()).readOnly && lastVal) {
         document.getElementById(lastItem.toString()).innerHTML = lastVal;
       }
    }
    //lastItem = 0;
    lastItem = i;
    lastVal = document.getElementById(i.toString()).innerHTML;

        if ( puzzle[i] == '0' )
            document.getElementById(i.toString()).className = "EnteredNumberFocused";
        else
            document.getElementById(i.toString()).className = "ClueNumberFocused";
        highlightItems(i);
    }
    return false;
}