I'm working on modernizing Rosetta Code's infrastructure. Starting with communications. Please accept this time-limited open invite to RC's Slack.. --Michael Mol (talk) 20:59, 30 May 2020 (UTC)

Black Box

From Rosetta Code
Black Box is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

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[edit]

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.

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())
}
}
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[edit]

Play it here.

 
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 = "&#x2688;";
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 = "&#x2688;";
} 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();
}
 


Julia[edit]

Gtk library GUI version.

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()
 

Nim[edit]

Translation of: Go
import random, sequtils, strutils
 
const WikiGame = true
 
type
 
Game = object
b: array[100, char] # displayed board.
h: array[100, char] # hidden atoms.
 
 
proc hideAtoms(game: var Game) =
var placed = 0
while placed < 4:
let a = rand(11..88)
let m = a mod 10
if m == 0 or m == 9 or game.h[a] == 'T':
continue
game.h[a] = 'T'
inc placed
 
 
proc initGame(): Game =
for i in 0..99:
result.b[i] = ' '
result.h[i] = 'F'
if not WikiGame:
result.hideAtoms()
else:
result.h[32] = 'T'
result.h[37] = 'T'
result.h[64] = 'T'
result.h[87] = 'T'
 
 
proc drawGrid(game: Game; score, guesses: Natural) =
echo " 0 1 2 3 4 5 6 7 8 9\n"
echo " ╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗"
echo "a $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $#".format(game.b[0..9].mapIt($it))
echo " ╔═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╗"
echo "b ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║".format(game.b[10..19].mapIt($it))
echo " ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣"
echo "c ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║".format(game.b[20..29].mapIt($it))
echo " ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣"
echo "d ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║".format(game.b[30..39].mapIt($it))
echo " ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣"
echo "e ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║".format(game.b[40..49].mapIt($it))
echo " ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣"
echo "f ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║".format(game.b[50..59].mapIt($it))
echo " ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣"
echo "g ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║".format(game.b[60..69].mapIt($it))
echo " ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣"
echo "h ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║".format(game.b[70..79].mapIt($it))
echo " ╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣"
echo "i ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║".format(game.b[80..89].mapIt($it))
echo " ╚═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╝"
echo "j $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $# ║ $#".format(game.b[90..99].mapIt($it))
echo " ╚═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╝"
 
let status = if guesses == 4: "Game over!" else: "In play"
echo "\n Score = ", score, "\tGuesses = ", guesses, "\t Status = ", status, '\n'
 
 
proc finalScore(game: var Game; score, guesses: Natural) =
var score = score
for i in 11..88:
let m = i mod 10
if m in [0, 9]: continue
if game.b[i] == 'G':
if game.h[i] == 'T':
game.b[i] = 'Y'
else:
game.b[i] = 'N'
inc score, 5
elif game.b[i] == ' ' and game.h[i] == 'T':
game.b[i] = 'A'
game.drawGrid(score, guesses)
 
 
func atCorner(ix: int): bool = ix in [0, 9, 90, 99]
 
func inRange(ix: int): bool = ix in 1..98 and ix notin [9, 90]
 
func atTop(ix: int): bool = ix in 1..8
 
func atBottom(ix: int): bool = ix in 91..98
 
func atLeft(ix: int): bool = ix.inRange and ix mod 10 == 0
 
func atRight(ix: int): bool = ix.inRange and ix mod 10 == 9
 
func inMiddle(ix: int): bool =
ix.inRange and not (ix.atTop or ix.atBottom or ix.atLeft or ix.atRight)
 
 
proc nextCell(game: Game): int =
while true:
stdout.write " Choose cell: "
stdout.flushFile()
try:
let sq = stdin.readLine().toLowerAscii
if sq == "q":
quit "Quitting.", QuitSuccess
if sq.len != 2 or sq[0] notin 'a'..'j' or sq[1] notin '0'..'9':
continue
result = int((ord(sq[0]) - ord('a')) * 10 + ord(sq[1]) - ord('0'))
if not result.atCorner: break
except EOFError:
echo()
quit "Encountered end of file. Quitting.", QuitFailure
echo()
 
 
proc play(game: var Game) =
var score, guesses = 0
var num = '0'
 
block outer:
while true:
block inner:
game.drawGrid(score, guesses)
let ix = game.nextCell()
if not ix.inMiddle and game.b[ix] != ' ': # already processed.
continue
var incr, def: int
if ix.atTop:
(incr, def) = (10, 1)
elif ix.atBottom:
(incr, def) = (-10, 1)
elif ix.atLeft:
(incr, def) = (1, 10)
elif ix.atRight:
(incr, def) = (-1, 10)
else:
if game.b[ix] != 'G':
game.b[ix] = 'G'
inc guesses
if guesses == 4: break outer
else:
game.b[ix] = ' '
dec guesses
continue
 
var first = true
var x = ix + incr
while x.inMiddle:
 
if game.h[x] == 'T':
# Hit.
game.b[ix] = 'H'
inc score
first = false
break inner
 
if first and (x + def).inMiddle and game.h[x + def] == 'T' or
(x - def).inMiddle and game.h[x - def] == 'T':
# Reflection.
game.b[ix] = 'R'
inc score
first = false
break inner
 
first = false
var y = x + incr - def
if y.inMiddle and game.h[y] == 'T':
# Deflection.
(incr, def) = if incr in [-1, 1]: (10, 1) else: (1, 10)
 
y = x + incr + def
if y.inMiddle and game.h[y] == 'T':
# Deflection or double deflection.
(incr, def) = if incr in [-1, 1]: (-10, 1) else: (-1, 10)
 
inc x, incr
 
num = if num != '9': succ(num) else: 'a'
if game.b[ix] == ' ': inc score
game.b[ix] = num
if x.inRange:
if ix == x:
game.b[ix] = 'R'
else:
if game.b[x] == ' ': inc score
game.b[x] = num
 
game.drawGrid(score, guesses)
game.finalScore(score, guesses)
 
 
proc main() =
 
randomize()
while true:
var game = initGame()
echo """
=== 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.
"""
 
game.play()
 
while true:
stdout.write " Play again (y/n): "
stdout.flushFile()
case stdin.readLine().toLowerAscii()
of "n": return
of "y": break
 
main()
Output:

Using same input as in Wren entry with 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

Phix[edit]

Library: Phix/pGUI

A configurable GUI version of the Black Box game, with a Knuth solver/helper.

-- 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()

Wren[edit]

Translation of: Go
Library: Wren-fmt
Library: Wren-ioutil
Library: Wren-str
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
}
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[edit]

Translation of: Go
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);
}
}

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