Implement a version of the Black Box game beginners configuration: 4 Atoms in an 8 x 8 grid.

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.

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>

zkl

Translation of: Go

<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(
  1. <<<"
   === 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");
  1. <<<

}

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