Black box
Implement a version of the Black Box game beginners configuration: 4 Atoms in an 8 x 8 grid.
Determine where the hidden atoms are in the box, by observing how the light beams fired into the box react when leaving it.
Possible results:
'H': the beam hit an atom and stopped
'R': Either the beam was reflected back the way it came or there was a ball just to one side of its entry point
'Numbers': indicate that the beam entered one of those squares and emerged from the other
Extra credit (Different game types):
-More or less atoms (maybe random)
-Different grid sizes
Go
Terminal based game.
Just the basic configuration - 4 atoms in an 8 x 8 grid.
To test it against known output (as opposed to playing a sensible game), the program has been fixed (wikiGame = true) to reproduce the atom position in the Wikipedia article's example game, followed by a complete set of beams and one incorrect and three correct guesses.
Set wikiGame to false to play a normal 'random' game. <lang go>package main
import (
"bufio" "fmt" "log" "math/rand" "os" "strings" "time"
)
var (
b = make([]rune, 100) // displayed board h = make([]rune, 100) // hidden atoms scanner = bufio.NewScanner(os.Stdin) wikiGame = true // set to false for a 'random' game
)
func initialize() {
for i := 0; i < 100; i++ { b[i] = ' ' h[i] = 'F' } if !wikiGame { hideAtoms() } else { h[32] = 'T' h[37] = 'T' h[64] = 'T' h[87] = 'T' } fmt.Println(` === BLACK BOX ===
H Hit (scores 1) R Reflection (scores 1) 1-9, Detour (scores 2) a-c Detour for 10-12 (scores 2) G Guess (maximum 4) Y Correct guess N Incorrect guess (scores 5) A Unguessed atom Cells are numbered a0 to j9. Corner cells do nothing. Use edge cells to fire beam. Use middle cells to add/delete a guess. Game ends automatically after 4 guesses. Enter q to abort game at any time. `)
}
func drawGrid(score, guesses int) {
fmt.Printf(" 0 1 2 3 4 5 6 7 8 9 \n") fmt.Printf("\n") fmt.Printf(" ╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗\n") fmt.Printf("a %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c\n", b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9]) fmt.Printf(" ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗\n") fmt.Printf("b ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║\n", b[10], b[11], b[12], b[13], b[14], b[15], b[16], b[17], b[18], b[19]) fmt.Printf(" ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣\n") fmt.Printf("c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║\n", b[20], b[21], b[22], b[23], b[24], b[25], b[26], b[27], b[28], b[29]) fmt.Printf(" ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣\n") fmt.Printf("d ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║\n", b[30], b[31], b[32], b[33], b[34], b[35], b[36], b[37], b[38], b[39]) fmt.Printf(" ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣\n") fmt.Printf("e ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║\n", b[40], b[41], b[42], b[43], b[44], b[45], b[46], b[47], b[48], b[49]) fmt.Printf(" ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣\n") fmt.Printf("f ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║\n", b[50], b[51], b[52], b[53], b[54], b[55], b[56], b[57], b[58], b[59]) fmt.Printf(" ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣\n") fmt.Printf("g ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║\n", b[60], b[61], b[62], b[63], b[64], b[65], b[66], b[67], b[68], b[69]) fmt.Printf(" ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣\n") fmt.Printf("h ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║\n", b[70], b[71], b[72], b[73], b[74], b[75], b[76], b[77], b[78], b[79]) fmt.Printf(" ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣\n") fmt.Printf("i ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║\n", b[80], b[81], b[82], b[83], b[84], b[85], b[86], b[87], b[88], b[89]) fmt.Printf(" ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝\n") fmt.Printf("j %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c ║ %c\n", b[90], b[91], b[92], b[93], b[94], b[95], b[96], b[97], b[98], b[99]) fmt.Printf(" ╚═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╝\n") status := "In play" if guesses == 4 { status = "Game over!" } fmt.Println("\n Score =", score, "\tGuesses =", guesses, "\t Status =", status, "\n")
}
func hideAtoms() {
placed := 0 for placed < 4 { a := 11 + rand.Intn(78) // 11 to 88 inclusive m := a % 10 if m == 0 || m == 9 || h[a] == 'T' { continue } h[a] = 'T' placed++ }
}
func nextCell() int {
var ix int for { fmt.Print(" Choose cell : ") scanner.Scan() sq := strings.ToLower(scanner.Text()) if len(sq) == 1 && sq[0] == 'q' { log.Fatal("program aborted") } if len(sq) != 2 || sq[0] < 'a' || sq[0] > 'j' || sq[1] < '0' || sq[1] > '9' { continue } ix = int((sq[0]-'a')*10 + sq[1] - 48) if atCorner(ix) { continue } break } check(scanner.Err()) fmt.Println() return ix
}
func atCorner(ix int) bool { return ix == 0 || ix == 9 || ix == 90 || ix == 99 }
func inRange(ix int) bool { return ix >= 1 && ix <= 98 && ix != 9 && ix != 90 }
func atTop(ix int) bool { return ix >= 1 && ix <= 8 }
func atBottom(ix int) bool { return ix >= 91 && ix <= 98 }
func atLeft(ix int) bool { return inRange(ix) && ix%10 == 0 }
func atRight(ix int) bool { return inRange(ix) && ix%10 == 9 }
func inMiddle(ix int) bool {
return inRange(ix) && !atTop(ix) && !atBottom(ix) && !atLeft(ix) && !atRight(ix)
}
func play() {
score, guesses := 0, 0 num := '0'
outer:
for { drawGrid(score, guesses) ix := nextCell() if !inMiddle(ix) && b[ix] != ' ' { // already processed continue } var inc, def int switch { case atTop(ix): inc, def = 10, 1 case atBottom(ix): inc, def = -10, 1 case atLeft(ix): inc, def = 1, 10 case atRight(ix): inc, def = -1, 10 default: if b[ix] != 'G' { b[ix] = 'G' guesses++ if guesses == 4 { break outer } } else { b[ix] = ' ' guesses-- } continue } var x int first := true for x = ix + inc; inMiddle(x); x += inc { if h[x] == 'T' { // hit b[ix] = 'H' score++ first = false continue outer } if first && (inMiddle(x+def) && h[x+def] == 'T') || (inMiddle(x-def) && h[x-def] == 'T') { // reflection b[ix] = 'R' score++ first = false continue outer } first = false y := x + inc - def if inMiddle(y) && h[y] == 'T' { // deflection switch inc { case 1, -1: inc, def = 10, 1 case 10, -10: inc, def = 1, 10 } } y = x + inc + def if inMiddle(y) && h[y] == 'T' { // deflection or double deflection switch inc { case 1, -1: inc, def = -10, 1 case 10, -10: inc, def = -1, 10 } } } if num != '9' { num++ } else { num = 'a' } if b[ix] == ' ' { score++ } b[ix] = num if inRange(x) { if ix == x { b[ix] = 'R' } else { if b[x] == ' ' { score++ } b[x] = num } } } drawGrid(score, guesses) finalScore(score, guesses)
}
func check(err error) {
if err != nil { log.Fatal(err) }
}
func finalScore(score, guesses int) {
for i := 11; i <= 88; i++ { m := i % 10 if m == 0 || m == 9 { continue } if b[i] == 'G' && h[i] == 'T' { b[i] = 'Y' } else if b[i] == 'G' && h[i] == 'F' { b[i] = 'N' score += 5 } else if b[i] == ' ' && h[i] == 'T' { b[i] = 'A' } } drawGrid(score, guesses)
}
func main() {
rand.Seed(time.Now().UnixNano()) for { initialize() play() inner: for { fmt.Print(" Play again y/n : ") scanner.Scan() yn := strings.ToLower(scanner.Text()) switch yn { case "n": return case "y": break inner } } check(scanner.Err()) }
}</lang>
- Output:
As the grid is displayed 29 times in all, this has been abbreviated to show just the first 2 and the last 3.
=== BLACK BOX === H Hit (scores 1) R Reflection (scores 1) 1-9, Detour (scores 2) a-c Detour for 10-12 (scores 2) G Guess (maximum 4) Y Correct guess N Incorrect guess (scores 5) A Unguessed atom Cells are numbered a0 to j9. Corner cells do nothing. Use edge cells to fire beam. Use middle cells to add/delete a guess. Game ends automatically after 4 guesses. Enter q to abort game at any time. 0 1 2 3 4 5 6 7 8 9 ╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗ a ║ ║ ║ ║ ║ ║ ║ ║ ║ ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗ b ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ c ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ d ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ e ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ f ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ g ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ h ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ i ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝ j ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╝ Score = 0 Guesses = 0 Status = In play Choose cell : b0 0 1 2 3 4 5 6 7 8 9 ╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗ a ║ ║ ║ ║ ║ ║ ║ ║ ║ ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗ b ║ 1 ║ ║ ║ ║ ║ ║ ║ ║ ║ 1 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ c ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ d ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ e ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ f ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ g ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ h ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ i ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝ j ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╝ Score = 2 Guesses = 0 Status = In play Choose cell : c0 ................ (Screens 3 to 26 omitted) ................ Score = 32 Guesses = 2 Status = In play Choose cell : g4 0 1 2 3 4 5 6 7 8 9 ╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗ a ║ 2 ║ H ║ 9 ║ H ║ 7 ║ 9 ║ H ║ 8 ║ ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗ b ║ 1 ║ ║ ║ ║ ║ ║ ║ ║ ║ 1 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ c ║ 2 ║ ║ ║ ║ ║ ║ ║ ║ ║ 8 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ d ║ H ║ G ║ ║ ║ ║ ║ ║ G ║ ║ H ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ e ║ 3 ║ ║ ║ ║ ║ ║ ║ ║ ║ 6 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ f ║ 4 ║ ║ ║ ║ ║ ║ ║ ║ ║ 7 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ g ║ H ║ ║ ║ ║ G ║ ║ ║ ║ ║ H ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ h ║ 5 ║ ║ ║ ║ ║ ║ ║ ║ ║ 6 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ i ║ H ║ ║ ║ ║ ║ ║ ║ ║ ║ H ║ ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝ j ║ 3 ║ H ║ 5 ║ H ║ 4 ║ R ║ H ║ R ║ ╚═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╝ Score = 32 Guesses = 3 Status = In play Choose cell : i7 0 1 2 3 4 5 6 7 8 9 ╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗ a ║ 2 ║ H ║ 9 ║ H ║ 7 ║ 9 ║ H ║ 8 ║ ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗ b ║ 1 ║ ║ ║ ║ ║ ║ ║ ║ ║ 1 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ c ║ 2 ║ ║ ║ ║ ║ ║ ║ ║ ║ 8 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ d ║ H ║ G ║ ║ ║ ║ ║ ║ G ║ ║ H ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ e ║ 3 ║ ║ ║ ║ ║ ║ ║ ║ ║ 6 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ f ║ 4 ║ ║ ║ ║ ║ ║ ║ ║ ║ 7 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ g ║ H ║ ║ ║ ║ G ║ ║ ║ ║ ║ H ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ h ║ 5 ║ ║ ║ ║ ║ ║ ║ ║ ║ 6 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ i ║ H ║ ║ ║ ║ ║ ║ ║ G ║ ║ H ║ ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝ j ║ 3 ║ H ║ 5 ║ H ║ 4 ║ R ║ H ║ R ║ ╚═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╝ Score = 32 Guesses = 4 Status = Game over! 0 1 2 3 4 5 6 7 8 9 ╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗ a ║ 2 ║ H ║ 9 ║ H ║ 7 ║ 9 ║ H ║ 8 ║ ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗ b ║ 1 ║ ║ ║ ║ ║ ║ ║ ║ ║ 1 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ c ║ 2 ║ ║ ║ ║ ║ ║ ║ ║ ║ 8 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ d ║ H ║ N ║ A ║ ║ ║ ║ ║ Y ║ ║ H ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ e ║ 3 ║ ║ ║ ║ ║ ║ ║ ║ ║ 6 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ f ║ 4 ║ ║ ║ ║ ║ ║ ║ ║ ║ 7 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ g ║ H ║ ║ ║ ║ Y ║ ║ ║ ║ ║ H ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ h ║ 5 ║ ║ ║ ║ ║ ║ ║ ║ ║ 6 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ i ║ H ║ ║ ║ ║ ║ ║ ║ Y ║ ║ H ║ ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝ j ║ 3 ║ H ║ 5 ║ H ║ 4 ║ R ║ H ║ R ║ ╚═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╝ Score = 37 Guesses = 4 Status = Game over! Play again y/n : n
JavaScript
Play it here. <lang javascript> var sel, again, check, score, done, atoms, guesses, beamCnt, brdSize;
function updateScore( s ) {
score += s || 0; para.innerHTML = "Score: " + score;
} function checkIt() {
check.className = "hide"; again.className = "again"; done = true; var b, id; for( var j = 0; j < brdSize; j++ ) { for( var i = 0; i < brdSize; i++ ) { if( board[i][j].H ) { b = document.getElementById( "atom" + ( i + j * brdSize ) ); b.innerHTML = "⚈"; if( board[i][j].T ) { b.style.color = "#0a2"; } else { b.style.color = "#f00"; updateScore( 5 ); } } } }
} function isValid( n ) {
return n > -1 && n < brdSize;
} function stepBeam( sx, sy, dx, dy ) {
var s = brdSize - 2 if( dx ) { if( board[sx][sy].H ) return {r:"H", x:sx, y:sy}; if( ( (sx == 1 && dx == 1) || (sx == s && dx == -1) ) && ( ( sy > 0 && board[sx][sy - 1].H ) || ( sy < s && board[sx][sy + 1].H ) ) ) return {r:"R", x:sx, y:sy}; if( isValid( sx + dx ) ) { if( isValid( sy - 1 ) && board[sx + dx][sy - 1].H ) { dx = 0; dy = 1; } if( isValid( sy + 1 ) && board[sx + dx][sy + 1].H ) { dx = 0; dy = -1; } sx += dx; return stepBeam( sx, sy, dx, dy ); } else { return {r:"O", x:sx, y:sy}; } } else { if( board[sx][sy].H ) return {r:"H", x:sx, y:sy}; if( ( (sy == 1 && dy == 1) || (sy == s && dy == -1) ) && ( ( sx > 0 && board[sx - 1][sy].H ) || ( sx < s && board[sx + 1][sy].H ) ) ) return {r:"R", x:sx, y:sy}; if( isValid( sy + dy ) ) { if( isValid( sx - 1 ) && board[sx - 1][sy + dy].H ) { dy = 0; dx = 1; } if( isValid( sx + 1 ) && board[sx + 1][sy + dy].H ) { dy = 0; dx = -1; } sy += dy; return stepBeam( sx, sy, dx, dy ); } else { return {r:"O", x:sx, y:sy}; } }
} function fireBeam( btn ) {
var sx = btn.i, sy = btn.j, dx = 0, dy = 0;
if( sx == 0 || sx == brdSize - 1 ) dx = sx == 0 ? 1 : - 1; else if( sy == 0 || sy == brdSize - 1 ) dy = sy == 0 ? 1 : - 1; var s = stepBeam( sx + dx, sy + dy, dx, dy ); switch( s.r ) { case "H": btn.innerHTML = "H"; updateScore( 1 ); break; case "R": btn.innerHTML = "R"; updateScore( 1 ); break; case "O": if( s.x == sx && s.y == sy ) { btn.innerHTML = "R"; updateScore( 1 ); } else { var b = document.getElementById( "fire" + ( s.x + s.y * brdSize ) ); btn.innerHTML = "" + beamCnt; b.innerHTML = "" + beamCnt; beamCnt++; updateScore( 2 ); } }
} function setAtom( btn ) {
if( done ) return; var b = document.getElementById( "atom" + ( btn.i + btn.j * brdSize ) ); if( board[btn.i][btn.j].T == 0 && guesses < atoms ) { board[btn.i][btn.j].T = 1; guesses++; b.innerHTML = "⚈"; } else if( board[btn.i][btn.j].T == 1 && guesses > 0 ) { board[btn.i][btn.j].T = 0; guesses--; b.innerHTML = " "; } if( guesses == atoms ) check.className = "check"; else check.className = "hide";
} function startGame() {
score = 0; updateScore(); check.className = again.className = "hide"; var e = document.getElementById( "mid" ); if( e.firstChild ) e.removeChild( e.firstChild ); brdSize = sel.value; done = false;
if( brdSize < 5 ) return;
var brd = document.createElement( "div" ); brd.id = "board"; brd.style.height = brd.style.width = 5.2 * brdSize + "vh" e.appendChild( brd ); var b, c, d; for( var j = 0; j < brdSize; j++ ) { for( var i = 0; i < brdSize; i++ ) { b = document.createElement( "button" ); b.i = i; b.j = j; if( j == 0 && i == 0 || j == 0 && i == brdSize - 1 || j == brdSize - 1 && i == 0 || j == brdSize - 1 && i == brdSize - 1 ) { b.className = "corner"; } else { if( j == 0 || j == brdSize - 1 || i == 0 || i == brdSize - 1 ) { b.className = "fire"; b.id = "fire" + ( i + j * brdSize ); } else { b.className = "atom"; b.id = "atom" + ( i + j * brdSize ); } b.addEventListener( "click", function( e ) { if( e.target.className == "fire" && e.target.innerHTML == " " ) fireBeam( e.target ); else if( e.target.className == "atom" ) setAtom( e.target ); }, false ); } b.appendChild( document.createTextNode( " " ) ); brd.appendChild( b ); } }
board = new Array( brdSize ); for( var j = 0; j < brdSize; j++ ) { board[j] = new Array( brdSize ); for( i = 0; i < brdSize; i++ ) { board[j][i] = {H: 0, T: 0}; } }
guesses = 0; beamCnt = 1; atoms = brdSize == 7 ? 3 : brdSize == 10 ? 4 : 4 + Math.floor( Math.random() * 5 );
var s = brdSize - 2, i, j; for( var k = 0; k < atoms; k++ ) { while( true ) { i = 1 + Math.floor( Math.random() * s ); j = 1 + Math.floor( Math.random() * s ); if( board[i][j].H == 0 ) break; } board[i][j].H = 1; }
} function init() {
sel = document.createElement( "select"); sel.options.add( new Option( "5 x 5 [3 atoms]", 7 ) ); sel.options.add( new Option( "8 x 8 [4 atoms]", 10 ) ); sel.options.add( new Option( "10 x 10 [4 - 8 atoms]", 12 ) ); sel.addEventListener( "change", startGame, false ); document.getElementById( "top" ).appendChild( sel ); check = document.createElement( "button" ); check.appendChild( document.createTextNode( "Check it!" ) ); check.className = "hide"; check.addEventListener( "click", checkIt, false ); again = document.createElement( "button" ); again.appendChild( document.createTextNode( "Again" ) ); again.className = "hide"; again.addEventListener( "click", startGame, false ); para = document.createElement( "p" ); para.className = "txt"; var d = document.getElementById( "bot" ); d.appendChild( para ); d.appendChild( check ); d.appendChild( again ); startGame();
} </lang>
Julia
Gtk library GUI version. <lang julia>using Colors, Cairo, Graphics, Gtk
struct BoxPosition
x::Int y::Int BoxPosition(i = 0, j = 0) = new(i, j)
end
@enum TrialResult Miss Hit Reflect Detour
struct TrialBeam
entry::BoxPosition exit::Union{BoxPosition, Nothing} result::TrialResult
end
function blackboxapp(boxlength=8, boxwidth=8, numballs=4)
r, turncount, guesses, guesscount, correctguesses = 20, 0, BoxPosition[], 0, 0 showballs, boxoffsetx, boxoffsety = false, r, r boxes = fill(colorant"wheat", boxlength + 4, boxwidth + 4) beamhistory, ballpositions = Vector{TrialBeam}(), Vector{BoxPosition}() win = GtkWindow("Black Box Game", 348, 800) |> (GtkFrame() |> (box = GtkBox(:v))) settingsbox = GtkBox(:v) playtoolbar = GtkToolbar()
newgame = GtkToolButton("New Game") set_gtk_property!(newgame, :label, "New Game") set_gtk_property!(newgame, :is_important, true)
reveal = GtkToolButton("Reveal") set_gtk_property!(reveal, :label, "Reveal Box") set_gtk_property!(reveal, :is_important, true)
map(w->push!(playtoolbar, w),[newgame, reveal])
scrwin = GtkScrolledWindow() can = GtkCanvas() set_gtk_property!(can, :expand, true) map(w -> push!(box, w),[settingsbox, playtoolbar, scrwin]) push!(scrwin, can)
function newgame!(w) empty!(ballpositions) empty!(guesses) empty!(beamhistory) guessing, showballs, guesscount, correctguesses = false, false, 0, 0 fill!(boxes, colorant"wheat") boxes[2, 3:end-2] .= boxes[end-1, 3:end-2] .= colorant"red" boxes[3:end-2, 2] .= boxes[3:end-2, end-1] .= colorant"red" boxes[3:end-2, 3:end-2] .= colorant"black" while length(ballpositions) < numballs p = BoxPosition(rand(3:boxlength+2), rand(3:boxwidth+2)) if !(p in ballpositions) push!(ballpositions, p) end end draw(can) end
@guarded draw(can) do widget ctx = Gtk.getgc(can) select_font_face(ctx, "Courier", Cairo.FONT_SLANT_NORMAL, Cairo.FONT_WEIGHT_BOLD) fontpointsize = 12 set_font_size(ctx, fontpointsize) # print black box graphic for i in 1:boxlength + 4, j in 1:boxwidth + 4 set_source(ctx, boxes[i, j]) move_to(ctx, boxoffsetx + i * r, boxoffsety + j * r) rectangle(ctx, boxoffsetx + i * r, boxoffsety + j * r, r, r) fill(ctx) p = BoxPosition(i, j) # show current guesses if p in guesses set_source(ctx, colorant"red") move_to(ctx, boxoffsetx + i * r + 2, boxoffsety + j * r + fontpointsize) show_text(ctx, p in ballpositions ? "+" : "-") stroke(ctx) end # show ball placements if reveal -> showballs if showballs && p in ballpositions set_source(ctx, colorant"green") circle(ctx, boxoffsetx + (i + 0.5) * r , boxoffsety + (j + 0.5) * r, 0.4 * r) fill(ctx) end end # draw dividing lines set_line_width(ctx, 2) set_source(ctx, colorant"wheat") for i in 4:boxlength + 2 move_to(ctx, boxoffsetx + i * r, boxoffsety + 3 * r) line_to(ctx, boxoffsetx + i * r, boxoffsety + (boxlength + 3) * r) stroke(ctx) end for j in 4:boxwidth + 2 move_to(ctx, boxoffsetx + 3 * r, boxoffsety + j * r) line_to(ctx, boxoffsetx + (boxlength + 3) * r, boxoffsety + j * r) stroke(ctx) end # show scoring update set_source(ctx, colorant"white") rectangle(ctx, 0, 305, 400, 50) fill(ctx) correct, incorrect = string(correctguesses), string(guesscount - correctguesses) score = string(2 * correctguesses - guesscount) set_source(ctx, colorant"black") move_to(ctx, 0, 320) show_text(ctx, " Correct: $correct Incorrect: $incorrect Score: $score") stroke(ctx) # show latest trial beams and results and trial history set_source(ctx, colorant"white") rectangle(ctx, 0, 360, 400, 420) fill(ctx) set_source(ctx, colorant"black") move_to(ctx, 0, 360) show_text(ctx, " Test Beam History") stroke(ctx) move_to(ctx, 0, 360 + fontpointsize * 1.5) show_text(ctx, " # Start Result End") stroke(ctx) for (i, p) in enumerate(beamhistory) move_to(ctx, 0, 360 + fontpointsize * (i + 1.5)) set_source(ctx, colorant"black") s = " " * rpad(i, 3) * rpad("($(p.entry.x - 2),$(p.entry.y - 2))", 8) * rpad(p.result, 12) * (p.exit == nothing ? " " : "($(p.exit.x - 2), $(p.exit.y - 2))") show_text(ctx, s) stroke(ctx) move_to(ctx, graphicxyfrombox(p.entry, 0.5 * fontpointsize)...) set_source(ctx, colorant"yellow") show_text(ctx, string(i)) stroke(ctx) if p.exit != nothing move_to(ctx, graphicxyfrombox(p.exit, 0.5 * fontpointsize)...) set_source(ctx, colorant"lightblue") show_text(ctx, string(i)) stroke(ctx) end end Gtk.showall(win) end
reveal!(w) = (showballs = !showballs; draw(can); Gtk.showall(win)) boxfromgraphicxy(x, y) = Int(round(x / r - 1.5)), Int(round(y / r - 1.5)) graphicxyfrombox(p, oset) = boxoffsetx + p.x * r + oset/2, boxoffsety + p.y * r + oset * 2 dirnext(x, y, dir) = x + dir[1], y + dir[2] rightward(d) = (-d[2], d[1]) leftward(d) = (d[2], -d[1]) rearward(direction) = (-direction[1], -direction[2]) ballfront(x, y, d) = BoxPosition(x + d[1], y + d[2]) in ballpositions ballright(x, y, d) = BoxPosition((dirnext(x, y, d) .+ rightward(d))...) in ballpositions ballleft(x, y, d) = BoxPosition((dirnext(x, y, d) .+ leftward(d))...) in ballpositions twocorners(x, y, d) = !ballfront(x, y, d) && ballright(x, y, d) && ballleft(x, y, d) enteringstartzone(x, y, direction) = atstart(dirnext(x, y, direction)...)
function atstart(x, y) return ((x == 2 || x == boxlength + 3) && (2 < y <= boxwidth + 3)) || ((y == 2 || y == boxwidth + 3) && (2 < x <= boxlength + 3)) end
function runpath(x, y) startp = BoxPosition(x, y) direction = (x == 2) ? (1, 0) : (x == boxlength + 3) ? (-1, 0) : (y == 2) ? (0, 1) : (0, -1) while true if ballfront(x, y, direction) return Hit, nothing elseif twocorners(x, y, direction) if atstart(x, y) return Reflect, startp end direction = rearward(direction) continue elseif ballleft(x, y, direction) if atstart(x, y) return Reflect, startp end direction = rightward(direction) continue elseif ballright(x, y, direction) if atstart(x, y) return Reflect, startp end direction = leftward(direction) continue elseif enteringstartzone(x, y, direction) x2, y2 = dirnext(x, y, direction) endp = BoxPosition(x2, y2) if x2 == startp.x && y2 == startp.y return Reflect, endp else if startp.x == x2 || startp.y == y2 return Miss, endp else return Detour, endp end end end x, y = dirnext(x, y, direction) @assert((2 < x < boxlength + 3) && (2 < y < boxwidth + 3)) end end
can.mouse.button1press = @guarded (widget, event) -> begin x, y = boxfromgraphicxy(event.x, event.y) # get click in blackbox area as a guess if 2 < x < boxlength + 3 && 2 < y < boxwidth + 3 p = BoxPosition(x, y) if !(p in guesses) push!(guesses, BoxPosition(x, y)) guesscount += 1 if p in ballpositions correctguesses += 1 end end draw(can) # test beam elseif atstart(x, y) result, endpoint = runpath(x, y) push!(beamhistory, TrialBeam(BoxPosition(x, y), endpoint, result)) if length(beamhistory) > 32 popfirst!(beamhistory) end draw(can) end end
condition = Condition() endit(w) = notify(condition) signal_connect(endit, win, :destroy) signal_connect(newgame!, newgame, :clicked) signal_connect(reveal!, reveal, :clicked)
newgame!(win) Gtk.showall(win) wait(condition)
end
blackboxapp() </lang>
Phix
A configurable GUI version of the Black Box game, with a Knuth solver/helper. <lang Phix>-- demo\rosetta\Black_Box.exw constant title = "Black Box", help_text = """ Discover the location of objects/atoms using the fewest probes/rays.
See distributed version for much longer help text and other comments. """ integer size, -- eg 8
s1, s2, -- size+1|2 count, -- eg 4 mask -- eg #0b100000 (first such >size^2-count+1) -- Note that new_game() contains limiting code.
sequence gameboard, -- actual, count 1's and size*size-count 0's.
eboard, -- one of "", as being enumerated through results, -- results of rays/probes, {x,y,c,x,y} format guessxy, -- locations (each element is {x,y}) guessclr, -- colours of "" (CD_BLUE for a guess, -- CD_GREEN for correct, -- CD_RED for wrong, -- CD_YELLOW/CYAN for hints. hidden, -- "" as saved during setup possibles, -- up to 635,376 integer codes for 8*8 with 4 game, -- each entry being possible for content of results, -- but never deliberately driven over 100,000. knowns, -- these "are" atoms (but "maybe" if tried<maxtry) minmaxmove -- best move available, see minmaxcount
integer possible, -- # of possibles checked to be plausible(), ie
-- [posssible+1..$] are all subject to imminent -- deletion by the idle handler, if invalid. hinted, -- # of probes analysed by hint_explore(). minmaxcount -- best (so far)
atom tried, maxtry -- # of enumerations attempted/theoretical max. bool hints_used = false -- (affects the scoring)
function probe(integer x, y, sequence board, bool bSort=true) -- -- returns {x,y,c,rx,ry} primarily for use in redraw_cb(), and -- secondarily for use in plausible(). -- where c is: -1 for reflection, 0 for hit, and +1 otherwise. -- Note that for the latter you need to allocate an actual colour -- elsewhere (if this did that it would spanner plausible() etc), -- and also note that -2 is now in use for the ray/probe hint. -- Also x,y and rx,ry re-ordered lowest-first to avoid duplicates, -- except for hint exploration, which passes a bSort of false. --
integer rx = x, ry = y, -- current/emerge point (ray) dx = 0, dy = 0, -- direction of travel moves = 0 -- debug aid
if x=0 then dx = +1 -- left entry, moving right elsif y=0 then dy = +1 -- top " down elsif x=s1 then dx = -1 -- right " left elsif y=s1 then dy = -1 -- btm " up else ?9/0 -- (sanity check) end if
while true do
integer nx = rx+dx, -- next logical position ny = ry+dy, idx = (ny-1)*size+nx
if nx=0 or nx=s1 or ny=0 or ny=s1 then if x=nx and y=ny then return {x,y,-1,0,0} -- Reflection elsif bSort then {{x,y},{nx,ny}} = sort({{x,y},{nx,ny}}) end if return {x,y,1,nx,ny} -- Emerges here elsif idx<=0 then ?9/0 -- (sanity check) elsif board[idx] then return {x,y,0,0,0} -- Hit -- -- aside: rather than check diagonally, nx/ny are -- simply discarded when a deflection occurs, -- and we actually check things laterally. -- elsif dx=0 then -- up/down movement, check sides if nx>1 and board[idx-1] then if nx<size and board[idx+1] then dy = -dy -- 180 else {dx,dy} = {1,0} -- right -- (yep, both up & down deflected -- right by an atom on the left) end if elsif nx<size and board[idx+1] then {dx,dy} = {-1,0} -- left -- (ditto left by one on the right) else {rx,ry} = {nx,ny} end if elsif dy=0 then -- left/right movement, check above/below if ny>1 and board[idx-size] then if ny<size and board[idx+size] then dx = -dx -- 180 else {dx,dy} = {0,1} -- down -- (yep, left & right are both -- deflected down by one above) end if elsif ny<size and board[idx+size] then {dx,dy} = {0,-1} -- up -- (ditto up by one below) else {rx,ry} = {nx,ny} end if else ?9/0 -- (sanity check, dx,dy=={0,0}!?) end if if rx=0 or rx=s1 or ry=0 or ry=s1 then {dx,dy} = {0,0} -- (outer swivel===reflection) end if -- guard against infinite loops, why not. -- *2 because swivel/move counted separately. moves += 1 if moves>2*size*size then ?9/0 end if end while
end function
function plausible(sequence board)
for i=1 to length(results) do sequence ri = results[i] integer {x,y} = ri if probe(x,y,board)!=ri then return false end if end for return true
end function
-- -- For the smaller games we could use almost any storage method, but to facilitate larger -- boards with more atoms we should be as stingy with memory as possible. To that end an -- enumeration is stored as a compact set of offsets to the next piece. For instance the -- board {0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,....1} is stored as offsets {4,6,8,64-18} -- further using an appropriate mask to give (((((46*#40)+8)*#40)+6)*#40)+4 which can be -- stored as a single integer/atom, yet unpacked quite easily, see next. Note there is -- code in new_game(), for valuechanged_cb(), that ensures we can store count*bits, and -- more by luck than judgement that (partly) helps avoid configurations that would take -- far longer than the universe has existed to enumerate and scan even just the once. -- function unpack(atom code)
sequence board = repeat(0,size*size) integer offset = 0, r, check = 0 while code do r = remainder(code,mask) if r<=0 then ?9/0 end if -- sanity check offset += r board[offset] = 1 code = floor(code/mask) check += 1 end while if check!=count then ?9/0 end if -- sanity check return board
end function
function pack(sequence board)
atom code = 0, pmask = 1 integer idx = 0, check = 0 while true do integer prev = idx idx = find(1,board,idx+1) if idx=0 then exit end if code = code + (idx-prev)*pmask check += 1 pmask *= mask end while if check!=count then ?9/0 end if -- sanity check
-- if unpack(code)!=board then ?9/0 end if -- ""
return code
end function
procedure trim_possibles() -- -- Re-process the possibles table as follows: -- 111...222322323...$ -- where 111... is possibly empty ok [1..possible], -- and 222322323 is some chunk [possible+1..limit], -- with 2s for oks and 3s for now-failing entries, -- which gets processed in a right-to-left order, -- such that fails(3) get replaced from the (1)s, -- being careful to quit early on any overlap, and -- /or re-test same slot if the 111... exhausted. -- Finally, trim off the dead head of possibles[]. -- The result is quite scrambled, but care we not. --
integer limit = min(possible+100_000,length(possibles)), limit0 = limit, kill = 1 -- (actually 1 over) while limit>max(possible,kill-1) do if not plausible(unpack(possibles[limit])) then possibles[limit] = possibles[kill] if kill<=possible then limit -= 1 end if kill += 1 else limit -= 1 end if end while possibles = possibles[kill..$] possible = limit0-kill+1
end procedure
procedure enumerate()
atom limit = min(tried+100_000,maxtry) while tried<limit and length(possibles)<100_000 do tried += 1 if plausible(eboard) then possibles &= pack(eboard) possible += 1 end if -- -- think abacus: find the first bead you can shift left, -- and slam the rest of them hard right. -- similar to binary counting, but you must always have -- exactly 'count' beads (ie 1's), eg -- choose(2*2,2) is 6: -- 0b0011 0b0101 0b0110 0b1001 0b1010 0b1100 -- -- However, because we are scanning from top left down -- to bottom right, it turned out better to do them in -- reverse order, hence shift right and slam left (not -- quite an exact mirror, but close enough). -- integer idx = find(1,eboard), last = 1 while true do eboard[idx] = 0 idx += 1 if idx>size*size then exit end if if eboard[idx]=0 then eboard[idx] = 1 exit end if eboard[last] = 1 last += 1 end while if idx=0 then exit end if end while
end procedure
function idx_from_edge(integer x,y) -- convert {x,y}, where one but not both of x,y are either 0 -- or s1, and the other is strictly 1..size, into 1..4*size. -- if x=0 then x = 0 -- (logically, but obvs. pointless)
if x=s1 then x = size elsif y=0 then y = size*2 elsif y=s1 then y = size*3 elsif x!=0 then ?9/0 end if -- not an edge?! return x+y
end function
function edge_from_idx(integer xy) -- convert 1..4*size into {0,1..size}/{s1,1..size}/{1..size,0}/{1..size,s1}
sequence res integer c = floor((xy-1)/size) switch c do case 0: res = {0,xy} case 1: res = {s1,xy-size} case 2: res = {xy-size*2,0} case 3: res = {xy-size*3,s1} default: ?9/0 end switch return res
end function
-- this is currently inlined, in case you were looking for it: --procedure idx_from_x_y(integer x, y) -- convert {1,1}..{size,size} to 1..size*size, for flat indexing -- return (y-1)*size+x --end function
function x_y_from_idx(integer idx) -- convert 1..size*size to {1,1}..{size,size} -- (absence of floor() on /size is a deliberate sanity check)
integer x = remainder(idx-1,size)+1, y = (idx-x)/size + 1 return {x,y}
end function
function next_hint()
sequence edges = repeat(0,size*4) integer x,y,r for i=1 to length(results) do {x,y,r} = results[i] for j=1 to 1+(r==1) do integer idx = idx_from_edge(x,y) if edges[idx] then ?9/0 end if edges[idx] = 1 {?,?,?,x,y} = results[i] end for end for integer new_hinted = find(0,edges,hinted+1) return new_hinted
end function
procedure explore_hints(integer new_hinted)
if new_hinted then -- originally, it proved better to scan these backwards... -- it now breaks (wrong tiles, I guess) if not flipped... new_hinted = size*4+1-new_hinted integer {x,y} = edge_from_idx(new_hinted), k sequence rxy = {}, counts = {} for i=1 to possible do sequence p = probe(x,y,unpack(possibles[i]),false) k = find(p,rxy) if k=0 then rxy = append(rxy,p) counts = append(counts,1) else counts[k] += 1 end if end for k = max(counts) if hinted=0 or minmaxcount=0 or k<minmaxcount then minmaxcount = k k = maxsq(counts,true) minmaxmove = rxy[k] minmaxmove[3] = -2 end if new_hinted = size*4+1-new_hinted -- unflip hinted = new_hinted else hinted = size*4 end if
end procedure
procedure find_common()
sequence all = repeat(1,size*size), none = repeat(0,size*size) for i=1 to possible do all = sq_and(all,unpack(possibles[i])) if all==none then exit end if end for knowns = {} for i=1 to length(all) do if all[i] then knowns = append(knowns,x_y_from_idx(i)) end if end for
end procedure
include pGUI.e Ihandle dlg, game_canvas, gridsize, atoms, score, hints, debug,
progress, declare
constant colour_table = {CD_RED,
CD_LIGHT_GREEN, CD_YELLOW, CD_BLUE, CD_ORANGE, CD_PURPLE, CD_CYAN, CD_MAGENTA, CD_GREEN, CD_DARK_GREEN, #bfef45, -- Lime #fabebe, -- Pink #469990, -- Teal #e6beff, -- Lavender #9A6324, -- Brown #fffac8, -- Beige #800000, -- Maroon #aaffc3, -- Mint #808000, -- Olive #ffd8b1, -- Apricot #000075} -- Navy
function colour(integer c)
c = mod(c-1,length(colour_table))+1 return colour_table[c]
end function
constant CD_HINTS = CD_DARK_GREY, -- (where to fire probe)
CD_MAYBE = CD_YELLOW, -- (probably an atom [scan not yet finished]) CD_KNOWN = CD_CYAN -- (known atoms [scan finished])
procedure redraw()
IupUpdate(game_canvas)
end procedure
function idle_action()
integer new_hinted = 0 if possible<length(possibles) then trim_possibles() hinted = 0 elsif tried<maxtry and length(possibles)<100_000 then enumerate() hinted = 0 elsif IupGetInt(hints,"VALUE") and hinted<size*4 then if possible>1 and hinted<size*4 then new_hinted = next_hint() explore_hints(new_hinted) redraw() end if if possible=1 or hinted=size*4 then hinted = size*4 find_common() redraw() end if else return IUP_IGNORE -- (disables idle) end if string title = sprintf("%,d / %,d (%d%%)",{possible,tried,100*(tried/maxtry)}) if new_hinted then title &= sprintf(", move %d/%d",{new_hinted,size*4}) end if IupSetStrAttribute(progress,"TITLE",title) return IUP_DEFAULT
end function constant idle_action_cb = Icallback("idle_action")
procedure start_idle()
IupSetAttribute(progress,"TITLE","-") IupSetGlobalFunction("IDLE_ACTION",idle_action_cb)
end procedure
procedure new_game()
size = IupGetInt(gridsize,"VALUE") s1 = size+1 s2 = size+2 count = IupGetInt(atoms,"VALUE") while true do -- in case count too big mask = #02 integer bits = 1 while mask<=size*size-count+1 do mask*=2 bits+=1 end while -- -- Prevent overflow: must be able to store count*bits in a Phix atom. -- count limits are therefore 13 on 5x5, 7 on 10x10, and 5 on 20x20, -- on 32-bit, but 64-bit does 16 on 5x5, 9 on 10x10, and 7 on 20x20. -- Many if not all of the silly-sized games this prohibits could not -- possibly be fully analysed within a typical human lifespan anyway. -- Besides just 5 atoms allows ambiguous/therefore unplayable games. -- See also the comments before unpack() above. Trying to store too -- many bits would trigger the sanity checks in pack()/unpack(). -- integer mb = iff(machine_bits()=32?53:64), maxcount = min(floor(mb/bits),size*size) if count<=maxcount then exit end if count = maxcount IupSetInt(atoms,"VALUE",count) end while
eboard = repeat(0,size*size) eboard[1..count] = 1 tried = 0 maxtry = choose(size*size,count) possibles = {} possible = 0 results = {} guessxy = {} guessclr = {} hidden = {} knowns = {} minmaxcount = 0 gameboard = repeat(0,size*size) bool active = IupGetInt(debug,"VALUE") integer done = 0, x, y, xy while done<count do x = rand(size) y = rand(size) xy = (y-1)*size+x if gameboard[xy]=0 then gameboard[xy] = 1 hidden = append(hidden,{x,y}) done += 1 elsif not find(0,gameboard) then ?9/0 -- let's not loop forever! -- (should now be prevented by maxcount above) end if end while IupSetInt(declare, "ACTIVE", active) if active then guessxy = hidden guessclr = repeat(CD_BLUE,length(guessxy)) end if hints_used = (IupGetInt(hints,"VALUE") and not active) start_idle()
end procedure
-- saved in redraw_cb(), for click testing in button_cb(): integer wh, -- width and height
mx, my -- margins
-- saved in declare_cb(), for adding to the score (10 each) integer wrong = 0
function redraw_cb(Ihandle ih, integer /*posx*/, integer /*posy*/)
integer {w,h} = IupGetIntInt(ih, "DRAWSIZE") -- calc width/height and margins (saved for button_cb): wh = min(floor((w-10)/s2),floor((h-10)/s2)) mx = floor((w-wh*(s2))/2) my = floor((h-wh*(s2))/2) cdCanvas cddbuffer = IupGetAttributePtr(ih,"DBUFFER") IupGLMakeCurrent(ih) cdCanvasActivate(cddbuffer) cdCanvasClear(cddbuffer)
-- outer edges (using one huge '+' shape) cdCanvasSetForeground(cddbuffer,CD_GREY) cdCanvasBox(cddbuffer,mx+wh,mx+wh*s1,my,my+wh*s2) cdCanvasBox(cddbuffer,mx,mx+wh*s2,my+wh,my+wh*s1) -- the inner size*size board (square) cdCanvasSetForeground(cddbuffer,CD_LIGHT_GREY) cdCanvasBox(cddbuffer,mx+wh,mx+wh*s1,my+wh,my+wh*s1) -- draw the grid lines cdCanvasSetForeground(cddbuffer,CD_WHITE) integer {lx,ly} = {mx,my} for i=1 to size+1 do lx += wh ly += wh cdCanvasLine(cddbuffer,lx,my,lx,my+wh*s2) cdCanvasLine(cddbuffer,mx,ly,mx+wh*s2,ly) end for
sequence edges = repeat(0,size*4) integer x,y,c = 1, h2 = floor(wh/2), r, rfrom = (minmaxcount==0 or IupGetInt(hints,"VALUE")=0) for i=rfrom to length(results) do {x,y,r} = iff(i=0?minmaxmove:results[i]) integer cb, ct string txt {txt,cb,ct} = iff(r=-2?{"+",CD_HINTS,CD_BLACK}: iff(r=-1?{"R",CD_WHITE,CD_BLACK}: iff(r==0?{"H",CD_BLACK,CD_WHITE}: {sprintf("%d",c),CD_GREY,colour(c)}))) for j=1 to 1+(r==1) do cdCanvasSetForeground(cddbuffer,cb) integer cx = mx+wh*x, cy = my+wh*(s1-y) cdCanvasBox(cddbuffer,cx+1,cx+wh,cy+1,cy+wh) cdCanvasSetForeground(cddbuffer,ct) cdCanvasFont(cddbuffer, "Helvetica", CD_BOLD, h2) cdCanvasText(cddbuffer, cx+h2, cy+h2, txt) if i!=0 then integer idx = idx_from_edge(x,y) if edges[idx] then ?9/0 end if edges[idx] = 1 if r=1 then {?,?,?,x,y} = results[i] c += (j=2) end if end if end for end for sequence gxy = guessxy, gclr = guessclr if IupGetInt(hints,"VALUE") then for i=1 to length(knowns) do sequence ki = knowns[i] if not find(ki,gxy) then gxy = append(gxy,ki) gclr = append(gclr,iff(tried<maxtry?CD_MAYBE:CD_KNOWN)) end if end for end if for i=1 to length(gxy) do {x,y} = gxy[i] atom cx = mx+(x+0.5)*wh, cy = my+(s1-y+0.5)*wh r = floor(wh*4/5) cdCanvasSetForeground(cddbuffer,gclr[i]) cdCanvasCircle(cddbuffer, cx, cy, r) end for cdCanvasFlush(cddbuffer)
-- IupSetStrAttribute(score,"TITLE","%d",{iff(hints_used?9999:sum(edges)+wrong*10)})
IupSetStrAttribute(score,"TITLE","%d",{sum(edges)+wrong*10}) return IUP_DEFAULT
end function
function map_cb(Ihandle ih)
IupGLMakeCurrent(ih) atom res = IupGetDouble(NULL, "SCREENDPI")/25.4 cdCanvas cddbuffer = cdCreateCanvas(CD_GL, "10x10 %g", {res}) IupSetAttributePtr(ih,"DBUFFER",cddbuffer) cdCanvasSetBackground(cddbuffer, CD_PARCHMENT) {} = cdCanvasTextAlignment(cddbuffer, CD_CENTER) return IUP_DEFAULT
end function
function canvas_resize_cb(Ihandle canvas)
cdCanvas cddbuffer = IupGetAttributePtr(canvas,"DBUFFER") integer {canvas_width, canvas_height} = IupGetIntInt(canvas, "DRAWSIZE") atom res = IupGetDouble(NULL, "SCREENDPI")/25.4 cdCanvasSetAttribute(cddbuffer, "SIZE", "%dx%d %g", {canvas_width, canvas_height, res}) return IUP_DEFAULT
end function
function declare_cb(Ihandle /*declare*/)
sequence add_h = repeat(true,length(hidden)) wrong = max(0,count-length(guessxy)) for i=1 to length(guessxy) do integer k = find(guessxy[i],hidden) if k then guessclr[i] = CD_GREEN add_h[k] = false else guessclr[i] = CD_RED wrong += 1 end if end for for i=1 to length(add_h) do if add_h[i] then guessxy = append(guessxy,hidden[i]) guessclr = append(guessclr,CD_BLUE) end if end for IupSetAttribute(declare, "ACTIVE", "NO") redraw() return IUP_DEFAULT
end function
function button_cb(Ihandle canvas, integer button, pressed, x, y, atom /*pStatus*/)
Ihandle frame = IupGetParent(canvas) string title = IupGetAttribute(frame,"TITLE") if button=IUP_BUTTON1 and not pressed then -- (left button released) x = floor((x-mx)/wh) y = floor((y-my)/wh) -- obviously, an x/y of 0 means left/top, -- whereas s1 means right/btm, -- and 1..size(both) is inner. bool outerx = (x>=0 and x<=s1), outery = (y>=0 and y<=s1), innerx = (x>=1 and x<=size), innery = (y>=1 and y<=size) if innerx and innery then sequence guess = {x,y} integer k = find(guess,guessxy) if k then guessxy[k..k] = {} guessclr[k..k] = {} else guessxy = append(guessxy,guess) guessclr = append(guessclr,CD_BLUE) end if bool bActive = (length(guessxy)==count) IupSetInt(declare, "ACTIVE", bActive) if IupGetInt(debug,"VALUE") and length(guessxy)=count then hidden = guessxy gameboard = repeat(0,size*size) for i=1 to count do {x,y} = hidden[i] integer xy = (y-1)*size+x gameboard[xy] = 1 end for results = {} end if redraw() elsif (outerx and innery) or (outery and innerx) then sequence r = probe(x,y,gameboard) if not find(r,results) then results = append(results,r) possible = 0 start_idle() end if redraw() end if end if return IUP_CONTINUE
end function
function new_game_cb(Ihandle /*ih*/)
new_game() redraw() return IUP_DEFAULT
end function
function exit_cb(Ihandle /*ih*/)
return IUP_CLOSE
end function
function help_cb(Ihandln /*ih*/)
IupMessage(title,help_text) return IUP_DEFAULT
end function
function key_cb(Ihandle /*dlg*/, atom c)
if c=K_ESC then return IUP_CLOSE end if if c=K_F1 then return help_cb(NULL) end if if c='?' then -- an old diagnostic that I kept in... for i=1 to min(5,length(possibles)) do sequence s = unpack(possibles[i]) for j=1 to size do ?s[1..size] s = s[size+1..$] end for puts(1,"\n") end for possible = 0 start_idle() end if return IUP_CONTINUE
end function
function valuechanged_cb(Ihandle ih)
if ih=hints then hints_used = true start_idle() else new_game() end if redraw() return IUP_DEFAULT
end function constant cb_valuechanged = Icallback("valuechanged_cb")
procedure main()
IupOpen() gridsize = IupText("SPIN=Yes, SPINMIN=1, SPINMAX=20, VALUE=8, RASTERSIZE=34x") atoms = IupText("SPIN=Yes, SPINMIN=1, SPINMAX=16, VALUE=4, RASTERSIZE=34x") score = IupLabel("","EXPAND=HORIZONTAL, PADDING=5x4") hints = IupToggle(" Show Hints?","VALUE=YES, RIGHTBUTTON=YES, PADDING=5x4") debug = IupToggle("Debug Mode?","VALUE=NO, RIGHTBUTTON=YES, PADDING=5x4") progress = IupLabel("-","EXPAND=HORIZONTAL, PADDING=5x4") declare = IupButton("Declare",Icallback("declare_cb"),"PADDING=5x4, ACTIVE=NO") game_canvas = IupGLCanvas("RASTERSIZE=400x400") Ihandle newgame = IupButton("New Game",Icallback("new_game_cb"),"PADDING=5x4"), help = IupButton("Help (F1)",Icallback("help_cb"),"PADDING=5x4"), quit = IupButton("E&xit",Icallback("exit_cb"),"PADDING=5x4"), vbox = IupVbox({IupHbox({IupLabel("Size","PADDING=5x4"),gridsize, IupFill(), IupLabel("Atoms","PADDING=5x4"),atoms}), IupHbox({hints,IupFill(),debug}), IupHbox({progress}), IupHbox({IupLabel("Score","PADDING=5x4"),score}), IupHbox({declare,newgame,help,quit})},"MARGIN=5x5"), game_frame = IupFrame(IupHbox({game_canvas},"MARGIN=3x3"),"TITLE=Game"), option_frame = IupFrame(vbox,"TITLE=Options"), full = IupHbox({game_frame,option_frame}) IupSetCallbacks({gridsize,atoms,hints,debug}, {"VALUECHANGED_CB", cb_valuechanged}) IupSetCallbacks(game_canvas, {"ACTION", Icallback("redraw_cb"), "MAP_CB", Icallback("map_cb"), "RESIZE_CB", Icallback("canvas_resize_cb"), "BUTTON_CB", Icallback("button_cb")}) dlg = IupDialog(IupHbox({full},"MARGIN=3x3")) IupSetAttribute(dlg, "TITLE", title) IupSetCallback(dlg, "K_ANY", Icallback("key_cb")) IupSetAttributeHandle(dlg,"DEFAULTENTER", declare) --erm...??
new_game()
IupShowXY(dlg,IUP_CENTER,IUP_CENTER) IupSetAttribute(dlg, "RASTERSIZE", NULL) IupSetStrAttribute(dlg, "MINSIZE", IupGetAttribute(dlg,"RASTERSIZE")) sequence fixsize = {score,progress} for i=1 to length(fixsize) do Ihandle fi = fixsize[i] IupSetAttributes(fi, "RASTERSIZE=%s, EXPAND=NO", {IupGetAttribute(fi,"RASTERSIZE")}) end for IupMainLoop() IupClose()
end procedure
main()</lang>
Wren
<lang ecmascript>import "random" for Random import "/fmt" for Fmt import "/ioutil" for Input import "/str" for Str
var b = List.filled(100, null) // displayed board var h = List.filled(100, null) // hidden atoms var wikiGame = true // set to false for a 'random' game var rand = Random.new()
var hideAtoms = Fn.new {
var placed = 0 while (placed < 4) { var a = rand.int(11, 89) // 11 to 88 inclusive var m = a % 10 if (m == 0 || m == 9 || h[a] == "T") continue h[a] = "T" placed = placed + 1 }
}
var initialize = Fn.new {
for (i in 0..99) { b[i] = " " h[i] = "F" } if (!wikiGame) { hideAtoms.call() } else { h[32] = "T" h[37] = "T" h[64] = "T" h[87] = "T" } System.print(""" === BLACK BOX === H Hit (scores 1) R Reflection (scores 1) 1-9, Detour (scores 2) a-c Detour for 10-12 (scores 2) G Guess (maximum 4) Y Correct guess N Incorrect guess (scores 5) A Unguessed atom Cells are numbered a0 to j9. Corner cells do nothing. Use edge cells to fire beam. Use middle cells to add/delete a guess. Game ends automatically after 4 guesses. Enter q to abort game at any time. """)
}
var drawGrid = Fn.new { |score, guesses|
System.print(" 0 1 2 3 4 5 6 7 8 9 ") System.print() System.print(" ╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗") Fmt.lprint("a $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s", [b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9]]) System.print(" ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗") Fmt.lprint ("b ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║", [b[10], b[11], b[12], b[13], b[14], b[15], b[16], b[17], b[18], b[19]]) System.print(" ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣") Fmt.lprint ("c ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║", [b[20], b[21], b[22], b[23], b[24], b[25], b[26], b[27], b[28], b[29]]) System.print(" ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣") Fmt.lprint ("d ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║", [b[30], b[31], b[32], b[33], b[34], b[35], b[36], b[37], b[38], b[39]]) System.print(" ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣") Fmt.lprint ("e ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║", [b[40], b[41], b[42], b[43], b[44], b[45], b[46], b[47], b[48], b[49]]) System.print(" ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣") Fmt.lprint ("f ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║", [b[50], b[51], b[52], b[53], b[54], b[55], b[56], b[57], b[58], b[59]]) System.print(" ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣") Fmt.lprint ("g ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║", [b[60], b[61], b[62], b[63], b[64], b[65], b[66], b[67], b[68], b[69]]) System.print(" ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣") Fmt.lprint ("h ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║", [b[70], b[71], b[72], b[73], b[74], b[75], b[76], b[77], b[78], b[79]]) System.print(" ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣") Fmt.lprint ("i ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║", [b[80], b[81], b[82], b[83], b[84], b[85], b[86], b[87], b[88], b[89]]) System.print(" ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝") Fmt.lprint ("j $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s ║ $s", [b[90], b[91], b[92], b[93], b[94], b[95], b[96], b[97], b[98], b[99]]) System.print(" ╚═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╝") var status = (guesses != 4) ? "In play" : "Game over!" System.print("\n Score = %(score)\tGuesses = %(guesses)\t Status = %(status)\n")
}
var atCorner = Fn.new { |ix| ix == 0 || ix == 9 || ix == 90 || ix == 99 }
var inRange = Fn.new { |ix| ix >= 1 && ix <= 98 && ix != 9 && ix != 90 }
var atTop = Fn.new { |ix| ix >= 1 && ix <= 8 }
var atBottom = Fn.new { |ix| ix >= 91 && ix <= 98 }
var atLeft = Fn.new { |ix| inRange.call(ix) && ix%10 == 0 }
var atRight = Fn.new { |ix| inRange.call(ix) && ix%10 == 9 }
var inMiddle = Fn.new { |ix|
return inRange.call(ix) && !atTop.call(ix) && !atBottom.call(ix) && !atLeft.call(ix) && !atRight.call(ix)
}
var nextCell = Fn.new {
var ix while (true) { var sq = Str.lower(Input.text(" Choose cell : ", 1)) if (sq.count == 1 && sq[0] == "q") { Fiber.abort("program aborted") } if (sq.count != 2 || !"abcdefghij".contains(sq[0]) || !"0123456789".contains(sq[1])) { continue } ix = (sq[0].bytes[0] - 97) * 10 + sq[1].bytes[0] - 48 if (atCorner.call(ix)) continue break } System.print() return ix
}
var finalScore = Fn.new { |score, guesses|
for (i in 11..88) { var m = i % 10 if (m == 0 || m == 9) continue if (b[i] == "G" && h[i] == "T") { b[i] = "Y" } else if (b[i] == "G" && h[i] == "F") { b[i] = "N" score = score + 5 } else if (b[i] == " " && h[i] == "T") { b[i] = "A" } } drawGrid.call(score, guesses)
}
var play = Fn.new {
var score = 0 var guesses = 0 var num = "0" while (true) { var outer = false drawGrid.call(score, guesses) var ix = nextCell.call() if (!inMiddle.call(ix) && b[ix] != " ") continue // already processed var inc var def if (atTop.call(ix)) { inc = 10 def = 1 } else if (atBottom.call(ix)) { inc = -10 def = 1 } else if (atLeft.call(ix)) { inc = 1 def = 10 } else if (atRight.call(ix)) { inc = -1 def = 10 } else { if (b[ix] != "G") { b[ix] = "G" guesses = guesses + 1 if (guesses == 4) break } else { b[ix] = " " guesses = guesses - 1 } continue } var x = ix + inc var first = true while (inMiddle.call(x)) { if (h[x] == "T" ) { // hit b[ix] = "H" score = score + 1 first = false outer = true break } if (first && (inMiddle.call(x+def) && h[x+def] == "T") || (inMiddle.call(x-def) && h[x-def] == "T")) { // reflection b[ix] = "R" score = score + 1 first = false outer = true break } first = false var y = x + inc - def if (inMiddle.call(y) && h[y] == "T") { // deflection if (inc.abs == 1) { inc = 10 def = 1 } else if (inc.abs == 10) { inc = 1 def = 10 } } y = x + inc + def if (inMiddle.call(y) && h[y] == "T") { // deflection or double deflection if (inc.abs == 1) { inc = -10 def = 1 } else if (inc.abs == 10) { inc = -1 def = 10 } } x = x + inc } if (outer) continue if (num != "9") { num = String.fromByte(num.bytes[0] + 1) } else { num = "a" } if (b[ix] == " ") score = score + 1 b[ix] = num if (inRange.call(x)) { if (ix == x) { b[ix] = "R" } else { if (b[x] == " ") score = score + 1 b[x] = num } } } drawGrid.call(score, guesses) finalScore.call(score, guesses)
}
while (true) {
initialize.call() play.call() var yn = Str.lower(Input.option(" Play again y/n : ", "ynYN")) if (yn == "n") return
}</lang>
- Output:
Sample game (wikiGame == true):
=== BLACK BOX === H Hit (scores 1) R Reflection (scores 1) 1-9, Detour (scores 2) a-c Detour for 10-12 (scores 2) G Guess (maximum 4) Y Correct guess N Incorrect guess (scores 5) A Unguessed atom Cells are numbered a0 to j9. Corner cells do nothing. Use edge cells to fire beam. Use middle cells to add/delete a guess. Game ends automatically after 4 guesses. Enter q to abort game at any time. 0 1 2 3 4 5 6 7 8 9 ╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗ a ║ ║ ║ ║ ║ ║ ║ ║ ║ ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗ b ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ c ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ d ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ e ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ f ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ g ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ h ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ i ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝ j ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╝ Score = 0 Guesses = 0 Status = In play Choose cell : b0 0 1 2 3 4 5 6 7 8 9 ╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗ a ║ ║ ║ ║ ║ ║ ║ ║ ║ ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗ b ║ 1 ║ ║ ║ ║ ║ ║ ║ ║ ║ 1 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ c ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ d ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ e ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ f ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ g ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ h ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ i ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝ j ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╝ Score = 2 Guesses = 0 Status = In play Choose cell : c0 0 1 2 3 4 5 6 7 8 9 ╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗ a ║ 2 ║ ║ ║ ║ ║ ║ ║ ║ ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗ b ║ 1 ║ ║ ║ ║ ║ ║ ║ ║ ║ 1 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ c ║ 2 ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ d ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ e ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ f ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ g ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ h ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ i ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝ j ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╝ Score = 4 Guesses = 0 Status = In play Choose cell : d7 0 1 2 3 4 5 6 7 8 9 ╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗ a ║ 2 ║ ║ ║ ║ ║ ║ ║ ║ ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗ b ║ 1 ║ ║ ║ ║ ║ ║ ║ ║ ║ 1 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ c ║ 2 ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ d ║ ║ ║ ║ ║ ║ ║ ║ G ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ e ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ f ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ g ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ h ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ i ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝ j ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╝ Score = 4 Guesses = 1 Status = In play Choose cell : d4 0 1 2 3 4 5 6 7 8 9 ╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗ a ║ 2 ║ ║ ║ ║ ║ ║ ║ ║ ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗ b ║ 1 ║ ║ ║ ║ ║ ║ ║ ║ ║ 1 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ c ║ 2 ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ d ║ ║ ║ ║ ║ G ║ ║ ║ G ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ e ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ f ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ g ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ h ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ i ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝ j ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╝ Score = 4 Guesses = 2 Status = In play Choose cell : e3 0 1 2 3 4 5 6 7 8 9 ╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗ a ║ 2 ║ ║ ║ ║ ║ ║ ║ ║ ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗ b ║ 1 ║ ║ ║ ║ ║ ║ ║ ║ ║ 1 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ c ║ 2 ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ d ║ ║ ║ ║ ║ G ║ ║ ║ G ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ e ║ ║ ║ ║ G ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ f ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ g ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ h ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ i ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝ j ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╝ Score = 4 Guesses = 3 Status = In play Choose cell : h2 0 1 2 3 4 5 6 7 8 9 ╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗ a ║ 2 ║ ║ ║ ║ ║ ║ ║ ║ ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗ b ║ 1 ║ ║ ║ ║ ║ ║ ║ ║ ║ 1 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ c ║ 2 ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ d ║ ║ ║ ║ ║ G ║ ║ ║ G ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ e ║ ║ ║ ║ G ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ f ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ g ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ h ║ ║ ║ G ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ i ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝ j ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╝ Score = 4 Guesses = 4 Status = Game over! 0 1 2 3 4 5 6 7 8 9 ╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗ a ║ 2 ║ ║ ║ ║ ║ ║ ║ ║ ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗ b ║ 1 ║ ║ ║ ║ ║ ║ ║ ║ ║ 1 ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ c ║ 2 ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ d ║ ║ ║ A ║ ║ N ║ ║ ║ Y ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ e ║ ║ ║ ║ N ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ f ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ g ║ ║ ║ ║ ║ A ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ h ║ ║ ║ N ║ ║ ║ ║ ║ ║ ║ ║ ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣ i ║ ║ ║ ║ ║ ║ ║ ║ A ║ ║ ║ ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝ j ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╝ Score = 19 Guesses = 4 Status = Game over! Play again y/n : n
zkl
<lang zkl>const ATM="A", F="F", HIT="H", G="G", GN="N", R="R", BLNK=" ", GY="Y";
var
brd,hdn, // displayed board & hidden atoms wikiGame = True; // set to False for a 'random' game
fcn initialize{
brd,hdn = List.createLong(100,BLNK), List.createLong(100,F);
if(not wikiGame) hideAtoms(); else hdn[32] = hdn[37] = hdn[64] = hdn[87] = ATM;
// else hdn[64] = hdn[66] = ATM; // Double deflection case
println(
- <<<"
=== BLACK BOX === H Hit (scores 1) R Reflection (scores 1) 1-9, Detour (scores 2) a-c Detour for 10-12 (scores 2) G Guess (maximum 4) Y Correct guess N Incorrect guess (scores 5) A Unguessed atom Cells are numbered a0 to j9. Corner cells do nothing. Use edge cells to fire beam. Use middle cells to add/delete a guess. Game ends automatically after 4 guesses. Enter q to abort game at any time.\n\n");
- <<<
}
fcn drawGrid(score, guesses){
var [const] vbs="\u2550\u2550\u2550\u256c", bt=(vbs.del(-3,3)), be1=String(" %s",vbs*7,bt,"%s").fmt, b1=be1("\u2554","\u2557"), e1=be1("\u255a","\u255d"),
be2=String(" %s", vbs*9, bt,"%s").fmt, b2=be2("\u2554", "\u2557"), b3=be2("\u256c", "\u256c"), e2=be2("\u255a", "\u255d"),
g1=String("%s%s ","\u2551 %s "*9).fmt, // a brd[0]=brd[90]=" " g2=String("%s ","\u2551 %s "*11).del(-3,3).fmt; // b c d .. i
println(" 0 1 2 3 4 5 6 7 8 9 \n",b1); grid,sep,n := g1, b2, -10; foreach c in (["a".."i"]){ println(grid(c,brd[n+=10,10].xplode())); println((c=="i") and e2 or sep); grid,sep = g2,b3; } println(g1("j",brd[90,10].xplode()), "\n", e1);
status:=(guesses==4) and "Game over!" or "In play"; println("\n Score = ", score, "\tGuesses = ", guesses, "\t Status = ", status);
}
fcn hideAtoms{
n:=4; do{ a,m:=(11).random(89), a % 10; // 11 to 88 inclusive if(m==0 or m==9 or hdn[a]==ATM) continue; hdn[a]=ATM; n-=1; }while(n);
}
fcn nextCell{
while(True){ s,c,n,sz := ask(" Choose cell [a-j][0-9]: ").strip().toLower(), s[0,1], s[1,1], s.len(); if(sz==1 and c=="q") System.exit(); if(not (sz==2 and ("a"<=c<="j") and ("0"<=n<="9"))) continue; ix:=10*(c.toAsc() - 97) + n; // row major, "a"-'a' if(not atCorner(ix)){ println(); return(ix); } }
}
fcn atCorner(ix){ ix==0 or ix==9 or ix==90 or ix==99 } fcn inRange(ix) { (1<=ix<=98) and ix!=9 and ix!=90 } fcn atTop(ix) { 1<=ix<= 8 } fcn atBottom(ix){ 91<=ix<=98 } fcn atLeft(ix) { inRange(ix) and ix%10 ==0 } fcn atRight(ix) { inRange(ix) and ix%10 ==9 } fcn inMiddle(ix){
inRange(ix) and not ( atTop(ix) or atBottom(ix) or atLeft(ix) or atRight(ix) )
} fcn play{
score,guesses,num := 0,0, 0x30; // '0' while(True){ drawGrid(score, guesses); ix:=nextCell(); if(not inMiddle(ix) and brd[ix]!=BLNK) continue; // already processed inc,def := 0,0; if (atTop(ix)) inc,def = 10, 1; else if(atBottom(ix)) inc,def = -10, 1; else if(atLeft(ix)) inc,def = 1, 10; else if(atRight(ix)) inc,def = -1, 10; else{
if(brd[ix]!=G){ brd[ix]=G; if( (guesses+=1) ==4) break(1); // you done }else{ brd[ix]=BLNK; guesses-=1; } continue;
} x,first := ix + inc, True; while(inMiddle(x)){
if(hdn[x]==ATM){ // hit brd[ix]=HIT; score+=1; first=False; continue(2); } if(first and (inMiddle(x + def) and hdn[x + def]==ATM) or (inMiddle(x - def) and hdn[x - def]==ATM)){ // reflection brd[ix]=R; score+=1; first=False; continue(2); } first=False; y:=x + inc - def; if(inMiddle(y) and hdn[y]==ATM){ // deflection switch(inc){ case( 1, -1){ inc, def = 10, 1 } case(10, -10){ inc, def = 1,10 } } } y=x + inc + def; if(inMiddle(y) and hdn[y]==ATM){ // deflection or double deflection switch(inc){ case( 1, -1){ inc, def = -10, 1 } case(10, -10){ inc, def = -1, 10 } } } x+=inc;
}// while inMiddle
if(brd[ix]==BLNK) score+=1; if(num!=0x39) num+=1; else num=97; // '0' & 'a' brd[ix]=num.toChar(); // right back at ya if(inRange(x)){
if(ix==x) brd[ix]=R; else{ if(brd[x]==BLNK) score+=1; brd[x]=num.toChar(); }
} } drawGrid( score, guesses); finalScore(score, guesses);
}
fcn finalScore(score, guesses){ println(hdn.toString(*));
foreach i in ([11..88]){ m:=i%10; if(m==0 or m==9) continue; if(brd[i]==G and hdn[i]==ATM) brd[i]=GY; else if(brd[i]==G and hdn[i]==F){ brd[i]=GN; score+=5; } else if(brd[i]==BLNK and hdn[i]==ATM) brd[i]=ATM; } drawGrid(score, guesses);
}
while(True){
initialize(); play(); while(True){ yn:=ask(" Play again y/n : ").strip().toLower(); if(yn=="n") break(2); else if(yn=="y") break(1); }
}</lang> Showing [results of] most of the Wikipedia actions:
- Output:
=== BLACK BOX === H Hit (scores 1) R Reflection (scores 1) 1-9, Detour (scores 2) a-c Detour for 10-12 (scores 2) G Guess (maximum 4) Y Correct guess N Incorrect guess (scores 5) A Unguessed atom Cells are numbered a0 to j9. Corner cells do nothing. Use edge cells to fire beam. Use middle cells to add/delete a guess. Game ends automatically after 4 guesses. Enter q to abort game at any time. 0 1 2 3 4 5 6 7 8 9 ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗ a ║ ║ ║ ║ ║ ║ ║ ║ ║ ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗ b ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬ c ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬ d ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬ e ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬ f ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬ g ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬ h ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬ i ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝ j ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝ Score = 0 Guesses = 0 Status = In play Choose cell [a-j][0-9]: g9 0 1 2 3 4 5 6 7 8 9 ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗ a ║ ║ ║ ║ ║ ║ ║ ║ ║ ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗ b ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬ c ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬ d ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬ e ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬ f ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬ g ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ H ║ ╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬ h ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬ i ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝ j ║ ║ ║ ║ ║ ║ ║ ║ ║ ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝ Score = 1 Guesses = 0 Status = In play ... Choose cell [a-j][0-9]: f0 0 1 2 3 4 5 6 7 8 9 ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗ a ║ ║ ║ 3 ║ ║ 1 ║ 3 ║ ║ ║ ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗ b ║ 2 ║ ║ ║ ║ ║ ║ ║ ║ ║ 2 ║ ╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬ c ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬ d ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬ e ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ 4 ║ ╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬ f ║ 5 ║ ║ ║ ║ ║ ║ ║ ║ ║ 1 ║ ╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬ g ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ H ║ ╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬ h ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ 4 ║ ╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬ i ║ ║ ║ ║ ║ ║ ║ G ║ ║ ║ ║ ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝ j ║ ║ ║ ║ ║ 5 ║ R ║ H ║ R ║ ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝ Score = 14 Guesses = 1 Status = In play