// jumpsaround.go - jumper game
// (c) 2020 Alexander Kulbartsch
// 
// Slightly performace optimized version: valid jumpes checked bevore iteration.
// added restricted multithreading

package main

import (
    "fmt"
    "sync"
    "sync/atomic"
)

const SIZEX = 6
const SIZEY = 5
const SPAWN = 2

type boardt [SIZEX][SIZEY]int32
var jump = [8][2]int32{{-1,-2}, {1,-2}, {-2,-1}, {2,-1}, {-2,1}, {2,1}, {-1,2}, {1,2}}
var solutions int32  = 0
var roundtrips int32 =  0

var wg sync.WaitGroup
var mutex sync.Mutex

func main() {
    fmt.Println("Hello, Jumper!")
	var board boardt
	jumpto(board, 0, 0, 1)
	wg.Wait() // waiting for waitgroup processes
	fmt.Println("===== Solution #", solutions," (Roundtripps:", roundtrips, ") ====== ")    
	fmt.Println("I am done.")
}


func jumpto(board boardt, x, y, iter int32) {

	if iter == (SPAWN + 1) {
		defer wg.Done() 
	}

	// ok - store position
	board[x][y] = iter
	
	// finished ?
	if iter >= SIZEX*SIZEY {

		// check for jumper roundtrips
	 	round := false
	 	for _, d := range jump {
	 		nx := x + d[0]
	 		ny := y + d[1]
	 		// check if position is on the board
			if nx<0 || nx>(SIZEX-1) || ny<0 || ny>(SIZEY-1) { continue }
			// detect roundtrip
			if board[nx][ny] == 1 { round = true; atomic.AddInt32(&roundtrips,1); break } // True
	 	}

        atomic.AddInt32(&solutions, 1)

		printboard(board, round)

		//board[x][y] = 0  // it's local
		
		return
	}
	
	// try next jump
    for _, d := range jump {
 		nx := x + d[0]
 		ny := y + d[1]

 		// check if position is on the board
		if nx<0 || nx>(SIZEX-1) || ny<0 || ny>(SIZEY-1) { continue }

        //  check if new field position is still empty
		if board[nx][ny] != 0 { continue } // True

		if iter == SPAWN {  // SPAWN = 2                                  // <<--
			wg.Add(1)
			go jumpto(board, nx, ny, iter + 1);                           // <<--
		} else { 
			jumpto(board, nx, ny, iter + 1);                              // <<--
		}
	}
	
	// this iteration is done
	board[x][y] = 0;
	
}


func printboard(board boardt, round bool) {
	mutex.Lock()                                                          // <<--
	if round == true { fmt.Println(" *** Roundtrip #", roundtrips, "*** ")
		fmt.Println("")
	    for	i := 0; i < SIZEY; i++ {
		    for j := 0; j < SIZEX; j++ {
				fmt.Printf("%2d:", board[j][i])
			}
			fmt.Println("")
		}
	    fmt.Println("")
	}
	mutex.Unlock()                                                        // <<--
}

// EOF
