Mystery Master

View a Logic Puzzle in JavaScript

Michael Benson


Table of Contents

Detective

Introduction

This article explains how to view a logic grid puzzle in the JavaScript programming language. If you have not already done so, please read my article "Model a Logic Puzzle in JavaScript". This article will focus on the logic puzzle "Five Houses". Please look at this logic puzzle before continuing. You should have a basic knowledge of JavaScript, know some of the newer constructs, and understand how classes work in JavaScript.

User Interface

One of the main concerns of any application is to have a responsive User Interface (UI). If the UI is unresponsive, the user will think the application has stopped working. A C# application can get around this problem by using the async/await keywords. Until these keywords are common in JavaScript, I use the following two technologies: (1) the setTimeout keyword, and (2) Web Workers. The setTimeout keyword is discussed towards the end of this article. Web Workers will be discussed in a future article. The component that manages the UI is called the Viewer.

Viewer

The Viewer component is the UI manager for Mystery Master. Because this is is a web application, it must manage HTML, CSS, and Javascript. Some of this is done using PHP. For instance, I use PHP to display common headers and footers for most of my pages. But the two most important visual components for solving a logic puzzle are the dashboard (aka Board), and the tabbed-interface (aka Tabby). These two components are displayed on the logic puzzle page after the text.

Note: Given everything I've said so far, I should probably take a look at React.

The Viewer Class

	
	

Board

The Board form displays information about the puzzle. Below is what the board looks like when there is no puzzle. If you want to know more about this form or any other form, please take a look at the Mystery Master Guide.

  Level   Pairs of Facts of   Hits
  Guess   Marks of Rules of   Hits

The board is initially displayed via the board.php file.

board.php

<table id="tblBoard" class="clsBoard">
	<tr>
		<td><button disabled id="btnSolve">Solve</button></td>
		<td>&nbsp; Level</td>
		<td><input type="text" readonly id="txtLevel" /></td>
		<td>&nbsp; Pairs</td>
		<td><input type="text" readonly id="txtNumPairs" /></td>
		<th>of</th>
		<td><input type="text" readonly id="txtMaxPairs" /></td>
		<td>Facts</td>
		<td><input type="text" readonly id="txtNumFacts" /></td>
		<th>of</th>
		<td><input type="text" readonly id="txtMaxFacts" /></td>
		<td>&nbsp; Hits</td>
		<td><input type="text" readonly id="txtNumFactHits" /></td>
	</tr>
	<tr>
		<td><button disabled id="btnReset">Reset</button></td>
		<td>&nbsp; Guess</td>
		<td><input type="text" readonly id="txtNumGuesses" /></td>
		<td>&nbsp; Marks</td>
		<td><input type="text" readonly id="txtNumMarks" /></td>
		<th>of</th>
		<td><input type="text" readonly id="txtMaxMarks" /></td>
		<td>Rules</td>
		<td><input type="text" readonly id="txtNumRules" /></td>
		<th>of</th>
		<td><input type="text" readonly id="txtMaxRules" /></td>
		<td>&nbsp; Hits</td>
		<td><input type="text" readonly id="txtNumRuleHits" /></td>
	</tr>
	<tr>
		<td colspan="13">
			<textarea rows="4" readonly id="txtMsg">
			</textarea>
		</td>
	</tr>
</table>

The board is updated by the Board class.

The Board Class

	
	

Tabby

The tabbed-interface controller, affectionately called tabby, displays one of many forms at a time. Here is what tabby looks like when the Setup form is displayed.

Nouns
Verbs
Facts
Rules
Marks
Chart
Grids
Statistics

work in progress...
Setup
Pauses
All
Level
Solution
Violation
Mark
Trigger
Guess
Placers
General
Autorun
Rechart
Rules
Triggers


Levels
All
Level 1
Level 2
Level 3
Level 4
Laws
All
Law 1
Law 2
Law 3
Law 4
Law 5

Tabby is initially displayed via the tabby.php file.

tabby.php

<div id="divTabby">
	<div id="divTabButtons">
		<button type="button" id="btnNouns">Nouns</button>
		<button type="button" id="btnVerbs">Verbs</button>
		<button type="button" id="btnLinks">Links</button>
		<button type="button" id="btnFacts">Facts</button>
		<button type="button" id="btnRules">Rules</button>
		<button type="button" id="btnMarks">Marks</button>
		<button type="button" id="btnChart">Chart</button>
		<button type="button" id="btnGrids">Grids</button>
		<button type="button" id="btnStats">Stats</button>
		<button type="button" id="btnSetup">Setup</button>
	</div>
	<div id="divTabContents">
		<div id="divNouns">Nouns</div>
		<div id="divVerbs">Verbs</div>
		<div id="divLinks">Links</div>
		<div id="divFacts">Facts</div>
		<div id="divRules">Rules</div>
		<div id="divMarks">Marks</div>
		<div id="divChart">Chart</div>
		<div id="divGrids">Grids</div>
		<div id="divStats">
			Statistics<br /><br />
			<i>work in progress...</i>
		</div>
		<div id="divSetup" style="display:inherit;">
			<?php include("setup.php") ?>
		</div>
	</div>
</div>

The Tabby Class

	
	

The forms for the nouns, verbs, links, facts, and rules are shown in my article "Model a Logic Puzzle in JavaScript". The forms for the marks, chart, and grids will be discussed in a future article. So that means the Stats form and the Setup form are discussed in this article.

Stats

The Statistics form displays information about how the puzzle was solved. It tells you how many marks and pairs were entered by level, law, and rule. Here are the statistics after our example puzzle has been solved.

The Stats Class

	
	

Setup

The Setup form displays options for the user that control how the application solves a logic puzzle. Here is what setup looks like when there is no puzzle.

Setup
Pauses
All
Level
Solution
Violation
Mark
Trigger
Guess
Placers
General
Autorun
Rechart
Rules
Triggers


Levels
All
Level 1
Level 2
Level 3
Level 4
Laws
All
Law 1
Law 2
Law 3
Law 4
Law 5

The Setup form is initially displayed via the setup.php file.

setup.php

<table class="clsAppTable clsSetup">
	<caption>Setup</caption>
	<tr>
		<td>
			<div class="clsSetupHeader">Pauses</div>
			<input type="checkbox" id="chkPauseAll">All<br />
			<input type="checkbox" id="chkPauseLevel">Level<br />
			<input type="checkbox" id="chkPauseSolution">Solution<br />
			<input type="checkbox" id="chkPauseViolation">Violation<br />
			<input type="checkbox" id="chkPauseMark">Mark<br />
			<input type="checkbox" id="chkPauseTrigger">Trigger<br />
			<input type="checkbox" id="chkPauseGuess">Guess<br />
			<input type="checkbox" id="chkPausePlacers">Placers<br />
		</td>
		<td>
			<div class="clsSetupHeader">General</div>
			<input type="checkbox" id="chkAutorun">Autorun<br />
			<input type="checkbox" id="chkRechart">Rechart<br />
			<input type="checkbox" id="chkRules">Rules<br />
			<input type="checkbox" id="chkTriggers">Triggers<br />
			<br />
			<button id="btnResetOptions">Reset</button><br />
		</td>
		<td>
			<div class="clsSetupHeader">Levels</div>
			<input type="checkbox" id="chkLevel0">All<br />
			<input type="checkbox" id="chkLevel1">Level 1<br />
			<input type="checkbox" id="chkLevel2">Level 2<br />
			<input type="checkbox" id="chkLevel3">Level 3<br />
			<input type="checkbox" id="chkLevel4">Level 4<br />
		</td>
		<td>
			<div class="clsSetupHeader">Laws</div>
			<input type="checkbox" id="chkLaw0">All<br />
			<input type="checkbox" id="chkLaw1">Law 1<br />
			<input type="checkbox" id="chkLaw2">Law 2<br />
			<input type="checkbox" id="chkLaw3">Law 3<br />
			<input type="checkbox" id="chkLaw4">Law 4<br />
			<input type="checkbox" id="chkLaw5">Law 5<br />
		</td>
	</tr>
</table>

The Setup Class

	
	

Locker

The Locker static class is a wrapper for HTML 5 Storage.

The Locker Class

	
	

Puzzler

The Puzzler static class contains helpful methods that return HTML as a string. For example, these methods are called to populate most of the forms in the tabbed-interface component.

The Puzzler Class

	
	

UIX

The User Interface and eXperience (UIX) static class for all pages. It has methods to show/hide the "fun" images that float on the right side of my pages.

The UIX Class

	
	

setTimeout

Here is an interesting article concerning setTimeout. To wit, JavaScript has a concurrency model based on an "event loop", and calling setTimeout adds a message to the queue. This technique avoids the dreaded "a script is taking too long to run" dialog.

Using setTimeout has its consequences - it turns a procedure-based program into an event-driven one. If the UI could be updated everytime a mark was entered, this would not be a problem. But there is one thing we must take into account - the user may want the program to pause for certain events, like when a mark is entered. If we say that each event places one or more tasks into a queue, we need a method that can process each task in order. Below is a simplified version of the doTasks method found in the Viewer class.

	function doTasks() {
		if (numTasks === maxTasks) return;
		let data = tasks[numTasks++];

		// Perform the task based on the key.
		switch (data.key) {
		}

		// Exit if the pause flag is true. Call resumeTasks when the Resume button is pressed.
		if (pauseFlag) {
			board.pauseWork();
			return;
		}

		// Recursively call doTasks via setTimeout.
		setTimeout(function () { doTasks(); }, ms);
	}
	

Sharp-minded people will notice this is similar to the Producer-Consumer problem. After the consumer performs a task, it must see if it needs to wait. If the consumer does not need to wait, it simply moves on to the next task in the queue. But if the consumer needs to wait, instead of spinning CPU cycles in a loop until the user presses the Resume button, the consumer simply stops running. This is the Not Runnable state. Now the only way for the consumer to resume is to have the click event of the Resume button tell the consumer to "wake up" and put the consumer into the Runnable state. I'm probably mixing metaphors here, but you get the idea.

Pausing

The user may request the program to pause in the situations given below.

Board Buttons

Here are all of the possible states for the Work and Quit buttons. Note that a button's caption may change for a specific state. For simplicity, the following refers to a button by its current caption.

  1. When there is no solver or no puzzle, both buttons are disabled.
  2. When the puzzle is loaded, the Work button is enabled and says "Solve".
  3. When the Solve button is pressed, the Solve button says "Pause" and the Quit button is enabled.
  4. When the Pause button is pressed, the Pause button says "Resume".
  5. When the Resume button is pressed, the Resume button says "Pause".
  6. When the Quit button is pressed, the Pause button says "Solve" and the Quit button says "Reset".
  7. When the Reset button is pressed, the Reset button says "Quit" and is disabled.

Warning: For DOM objects with an id, most browsers will auto-create JavaScript variables with the id as its name! I don't like this because I do not want the viewer to access fields that are managed by subcomponents.

Conclusion

I hope you enjoyed reading this article. My motivation for writing this article is that you will try to model a logic puzzle on your own. Then together we can find better ways to model and/or solve logic puzzles. Thank you.

About the Author

Michael Benson Michael Benson

My current position is zookeeper of the Mystery Master website at http://www.mysterymaster.com. My favorite languages are C#, Java, and JavaScript. When I'm not at my computer, I am travelling far more than I like to places far more exciting than I can handle.