; --------------------------------
; Name: Othello
; Author: Jautois
; Version: 1.0.1 Beta - stable ?
; Date: March. 9, 2011
; --------------------------------

#include <Array.au3>

HotKeySet("{ESC}", "_Exit")

Global $DEBUG = False
Global $ComputerThinks = True
Global $gL_x = 8, $gL_y = $gL_x
Global $board[$gL_x][$gL_y]
Global Const $lHn = "     1   2   3   4   5   6   7   8 - X"
Global Const $lH = "   + - + - + - + - + - + - + - + - +"
Global Const $lV = "   |   |   |   |   |   |   |   |   |"
Global $lVS = "|"
Global $turnnr = 0

;- Clear Scite Output
Send("+{F5}")

;-
ConsoleWrite('>> Wilkommen zu Othello 1.0.1 Beta von Johannes' & @CRLF)
ConsoleWrite('>> Erstellt March. 9, 2011' & @CRLF)

While True
	_BoardReset()
	_BoardStartup()
	$tiles = _SetPlayerCoins()
	Local $PlayerCoin = $tiles[0], $ComputerCoin = $tiles[1]
	$showcheat = False
	$turn = _SetStartPlayer()
	ConsoleWrite(@CRLF & ">> " & $turn & ' darf beginnen ...' & @CRLF & @CRLF)
	While True
		If $turn == 'Spieler' Then
			If $showcheat Then
				$valMovesBoard = _BoardGetValidMoved($board, $PlayerCoin)
				_BoardDraw($valMovesBoard)
			Else
				_BoardDraw($board)
			EndIf
			_PointsShow()
			$move = _MovePlayer($board, $PlayerCoin)
			If IsArray($move) Then
				If $DEBUG Then _Debug('Main Loop', '$move is an array!', @ScriptLineNumber, ">")
				If _MakeMove($board, $PlayerCoin, $move[0], $move[1]) Then
					If $DEBUG Then _Debug('Main Loop', 'Successfully MOVED!', @ScriptLineNumber, "+")
				Else
					If $DEBUG Then _Debug('Main Loop', 'Error NOT MOVED!', @ScriptLineNumber, "!")
				EndIf
			Else
				If $DEBUG Then _Debug('Main Loop', '$move is a word!', @ScriptLineNumber, ">")
				If $move = 'quit' Then
					ConsoleWrite('!> Danke fürs Spielen =)' & @CRLF & "!> Bis bald" & @CRLF)
					Sleep(2000)
					Exit
				ElseIf $move = 'cheat' Then
					$showcheat = Not $showcheat
					ContinueLoop
				EndIf
			EndIf
			$ar = _VaildMoves($board, $ComputerCoin)
			If UBound($ar) = 0 Then
				ExitLoop
			Else
				$turn = 'Computer'
			EndIf
		Else
			_BoardDraw($board)
			_PointsShow()
			ConsoleWrite(">> Der Computer ist an der Reihe:" & @CRLF)
			$ar_com = _MoveComputer($board, $ComputerCoin)
			_MakeMove($board, $ComputerCoin, $ar_com[0], $ar_com[1])
			$ar = _VaildMoves($board, $PlayerCoin)
			If UBound($ar) = 0 Then
				ExitLoop
			Else
				$turn = 'Spieler'
			EndIf
		EndIf

		$turnnr += 1
		ConsoleWrite(@CRLF & ">> Zug: " & $turnnr & @CRLF)
		Send("+{F5}")
	WEnd
	_BoardDraw($board)
	$scores = _BoardScore($board)
	ConsoleWrite(@CRLF & '__________SCORES__________' & @CRLF)
	ConsoleWrite('Spieler X: ' & $scores[0] & ' Punkte' & @CRLF & 'Spieler O: ' & $scores[1] & ' Punkte' & @CRLF)
	If $scores[0] > $scores[1] Then
		ConsoleWrite('Du hast den Computer mit ' & ($scores[0] - $scores[1]) & ' Punkten mehr geschlagen! Glückwunsch!' & @CRLF)
	ElseIf $scores[0] < $scores[1] Then
		ConsoleWrite('Du hast verloren!. Der Computer hat dich mit ' & ($scores[1] - $scores[0]) & ' Punkten mehr geschlagen!.' & @CRLF)
	Else
		ConsoleWrite('Das Spiel ist unentschieden!' & @CRLF)
	EndIf
	ConsoleWrite(@CRLF & '___________________________' & @CRLF)

	If Not _NewGame() Then
		ExitLoop
	EndIf
WEnd

Func _BoardStartup()
	$board[3][3] = 'X'
	$board[3][4] = 'O'
	$board[4][3] = 'O'
	$board[4][4] = 'X'
EndFunc

Func _BoardDraw($board, $mode = 1)
	; I got some bugs here too? - double-check!
	ConsoleWrite($lHn & @CRLF & $lH & @CRLF)
	For $i = 0 To $gL_y - 1
		If $mode = 0 Then
			ConsoleWrite($lV & @CRLF)
		Else
			ConsoleWrite($i + 1 & " ")
			For $j = 0 To $gL_x - 1
				If $board[$j][$i] <> " " Then
					ConsoleWrite(" " & $lVS & " " & $board[$j][$i])
				Else
					ConsoleWrite(" " & $lVS & "  ")
				EndIf
			Next
			ConsoleWrite(" " & $lVS & @CRLF)
		EndIf
		ConsoleWrite($lH & @CRLF)
	Next
	ConsoleWrite(" |" & @CRLF & " Y" & @CRLF)
EndFunc   ;==>_BoardDraw

Func _BoardReset()
	For $i = 0 To $gL_y - 1
		For $j = 0 To $gL_x - 1
			$board[$j][$i] = " "
		Next
	Next
EndFunc   ;==>_BoardReset

Func _MoveIsValid($board, $tile, $x_s, $y_s)
	If $DEBUG Then _Debug('_MoveIsValid', ">tile:" & $tile & " >xstart:" & $x_s & " >ystart:" & $y_s, @ScriptLineNumber, ">")
	If $board[$x_s][$y_s] <> " " Or Not _OnBoard($x_s, $y_s) Then Return False
	$board[$x_s][$y_s] = $tile
	If $tile == "X" Then
		$ocoin = "O"
	Else
		$ocoin = "X"
	EndIf
	If $DEBUG Then _Debug('_MoveIsValid', ">othertile:" & $ocoin, @ScriptLineNumber, ">")
	Local $directions[$gL_x][2] = [[0, 1],[1, 1],[1, 0],[1, -1],[0, -1],[-1, -1],[-1, 0],[-1, 1]]
	;_ArrayDisplay($directions)
	Local $coinsSet[1][2]
	Local $ar_index = 0

	For $i = 0 To $gL_y - 1
		$_x = $x_s
		$_y = $y_s
		$_x += $directions[$i][0]
		$_y += $directions[$i][1]
		If _OnBoard($_x, $_y) And $board[$_x][$_y] == $ocoin Then
			If $DEBUG Then _Debug('_MoveIsValid', "PASSED!", @ScriptLineNumber, "+")
			$_x += $directions[$i][0]
			$_y += $directions[$i][1]
			If Not _OnBoard($_x, $_y) Then ContinueLoop
			While $board[$_x][$_y] == $ocoin
				$_x += $directions[$i][0]
				$_y += $directions[$i][1]
				; holy smoke! Seems to be buggy with $directions > 2 And if Not Passed // double-check!
				If Not _OnBoard($_x, $_y) Then ExitLoop
			WEnd
			If Not _OnBoard($_x, $_y) Then ContinueLoop
			If $board[$_x][$_y] == $tile Then
				While True
					$_x -= $directions[$i][0]
					$_y -= $directions[$i][1]
					If $_x == $x_s And $_y == $y_s Then
						If $DEBUG Then _Debug('_MoveIsValid', "Invalid Turn >x:" & $_x & " >y:" & $_y, @ScriptLineNumber, "!")
						ExitLoop
					EndIf
					If $DEBUG Then _Debug('_MoveIsValid', "Valid Turn >x:" & $_x & " >y:" & $_y, @ScriptLineNumber, "+")
					$coinsSet[$ar_index][0] = $_x
					$coinsSet[$ar_index][1] = $_y
					;_ArrayDisplay($coinsSet)
					$ar_index += 1
					ReDim $coinsSet[$ar_index + 1][2] ; Maybe change this to improve line 184-186
				WEnd
			EndIf
		EndIf
	Next

	$board[$x_s][$y_s] = " "

	; MESSY AND UGLY!! Change that! --->
	For $i = 0 To UBound($coinsSet) - 1
		If $coinsSet[$i][0] = "" Then _ArrayDelete($coinsSet, $i)
	Next
	;<---

	If $DEBUG Then _Debug('_MoveIsValid', UBound($coinsSet, 1), @ScriptLineNumber, ">")
	If UBound($coinsSet, 1) = 0 Then
		Return False
	Else
		Return $coinsSet
	EndIf
EndFunc   ;==>_MoveIsValid


Func _OnBoard($x, $y)
	If $x >= 0 And $x <= 7 And $y >= 0 And $y <= 7 Then
		If $DEBUG Then _Debug("_OnBoard", 'Ist auf dem Spielfeld!', @ScriptLineNumber, "+")
		Return True
	Else
		If $DEBUG Then _Debug("_OnBoard", 'Ist NICHT auf dem Spielfeld!', @ScriptLineNumber, "!")
		Return False
	EndIf
EndFunc   ;==>_OnBoard


Func _VaildMoves($board, $tile)
	Local $valMoves[1][2]
	Local $ar_index = 0

	For $i = 0 To $gL_y - 1
		For $j = 0 To $gL_x - 1
			If _MoveIsValid($board, $tile, $j, $i) <> False Then
				$valMoves[$ar_index][0] = $j
				$valMoves[$ar_index][1] = $i
				$ar_index += 1
				ReDim $valMoves[$ar_index + 1][2]
			EndIf
		Next
	Next

	; MESSY AND UGLY!! Change that! --->
	For $i = 0 To UBound($valMoves) - 1
		If $valMoves[$i][0] = "" Then _ArrayDelete($valMoves, $i)
	Next
	;<---

	;_ArrayDisplay($valMoves)
	Return $valMoves
EndFunc   ;==>_VaildMoves

Func _BoardGetValidMoved($board, $tile)
	Local $dboard = _CopyBoard($board)
	$valid = _VaildMoves($dboard, $tile)
	For $i = 0 To UBound($valid, 1) - 1
		$dboard[$valid[$i][0]][$valid[$i][1]] = '!'
	Next
	;_ArrayDisplay($dboard)
	Return $dboard
EndFunc   ;==>_BoardGetValidMoved

Func _BoardScore($board)
	Local $xscore = 0
	Local $oscore = 0
	Local $points[2]

	For $i = 0 To $gL_y - 1
		For $j = 0 To $gL_x - 1
			If $board[$j][$i] == 'X' Then $xscore += 1
			If $board[$j][$i] == 'O' Then $oscore += 1
		Next
	Next

	$points[0] = $xscore
	$points[1] = $oscore
	If $DEBUG Then _Debug('_BoardScore', '>X:" & $xscore & " >O:" & $oscore', @ScriptLineNumber, ">")
	Return $points
EndFunc   ;==>_BoardScore

Func _SetPlayerCoins()
	Local $tile = ''
	Local $data[2]

	While not ($tile == 'X' Or $tile == 'O')
		$tile = InputBox("Info", 'Was möchtest du spielen? X oder O?')
		$tile = StringUpper($tile)
	WEnd

	If $tile == 'X' Then
		$data[0] = "X"
		$data[1] = "O"
	Else
		$data[0] = "O"
		$data[1] = "X"
	EndIf
	Return $data
EndFunc   ;==>_SetPlayerCoins

Func _SetStartPlayer()
	If Random(0, 1, 1) = 0 Then
		$s = 'Computer'
	Else
		$s = 'Spieler'
	EndIf
	Return $s
EndFunc   ;==>_SetStartPlayer

Func _NewGame()
	$input = InputBox("Info", 'Nochmal? (yes or no)')
	If StringLeft($input, 1) = "y" Then
		Return True
	Else
		Return False
	EndIf
EndFunc   ;==>_NewGame

Func _MakeMove(ByRef $board, $tile, $x_s, $y_s)
	Local $coinsSet = _MoveIsValid($board, $tile, $x_s, $y_s)
	If $coinsSet = False Then Return False
	$board[$x_s][$y_s] = $tile
	For $i = 0 To UBound($coinsSet) - 1
		$board[$coinsSet[$i][0]][$coinsSet[$i][1]] = $tile
	Next
	Return True
EndFunc   ;==>_MakeMove

Func _CopyBoard($board)
	Local $dboard = $board

	For $i = 0 To $gL_y - 1
		For $j = 0 To $gL_x - 1
			$dboard[$j][$i] = $board[$j][$i]
		Next
	Next
	Return $dboard
EndFunc   ;==>_CopyBoard

Func _IsInCorner($x, $y)
	If ($x = 0 And $y = 0) or ($x = 7 And $y = 0) or ($x = 0 And $y = 7) or ($x = 7 And $y = 7) Then
		If $DEBUG Then _Debug('_IsInCorner', 'Coin can move to a corner!', @ScriptLineNumber, "+")
		Return True
	Else
		If $DEBUG Then _Debug('_IsInCorner', "Coin can't move to a corner!", @ScriptLineNumber, "!")
		Return False
	EndIf
EndFunc   ;==>_IsInCorner

Func _MovePlayer($board, $PlayerCoin)
	Local $poss_z[8] = [1,2,3,4,5,6,7,8]
	Local $coords[2]

	While True
		$input = InputBox("Info", 'Dein Zug z.B. 11 oder 24 (XY)' & @CRLF & '"quit" -> zum Beenden' & @CRLF & '"cheat" -> an/aus ')
		$move = StringLower($input)
		If $move = 'quit' Then
			Return 'quit'
		EndIf
		If $move = 'cheat' Then
			Return 'cheat'
		EndIf
		If StringLen($move) == 2 Then
			$sp = StringSplit($move, "", 2)
			$one = _ArrayBinarySearch($poss_z, $sp[0])
			$sec = _ArrayBinarySearch($poss_z, $sp[1])
			If $one <> -1 And $sec <> -1 Then
				$x = Int($sp[0]) - 1
				$y = Int($sp[1]) - 1

				If _MoveIsValid($board, $PlayerCoin, $x, $y) == False Then
					If $DEBUG Then _Debug('_MovePlayer', "ContinueLoop", @ScriptLineNumber, "!")
					ConsoleWrite("!> Ungültiger Zug - (XY) Beispiel: 32" & @CRLF)
					ContinueLoop
				Else
					If $DEBUG Then _Debug('_MovePlayer', "ExitLoop", @ScriptLineNumber, "!")
					ExitLoop
				EndIf
			Else
				MsgBox(64, 'Info', 'Das ist kein gültiger Zug!')
			EndIf
		EndIf
	WEnd
	$coords[0] = $x
	$coords[1] = $y
	If $DEBUG Then _Debug('_MovePlayer', ">x:" & $coords[0] & " >y:" & $coords[1], @ScriptLineNumber, "!")

	Return $coords
EndFunc   ;==>_MovePlayer

Func _MoveComputer($board, $ComputerCoin)
	Local $possMoves = _VaildMoves($board, $ComputerCoin)
	Local $coords[2], $bestMove[2]
	_2DArrayShuffle($possMoves)

	For $i = 0 To UBound($possMoves) - 1
		If _IsInCorner($possMoves[$i][0], $possMoves[$i][1]) Then
			$coords[0] = $possMoves[$i][0]
			$coords[1] = $possMoves[$i][1]
			Return $coords
		EndIf
	Next
	$bestScore = -1
	ConsoleWrite(">> Computer überlegt ")
	$count = UBound($possMoves) - 1
	For $i = 0 To $count
		ConsoleWrite(Round((($i+1)/($count+1))*100,2) & "% > ")
		If $ComputerThinks Then Sleep(500)
		$x = $possMoves[$i][0]
		$y = $possMoves[$i][1]

		$dboard = _CopyBoard($board)

		_MakeMove($dboard, $ComputerCoin, $x, $y)

		$score = _BoardScore($dboard)
		$sp = $score[1]
		If $ComputerCoin == "X" Then $sp = $score[0]

		If $sp > $bestScore Then
			$bestMove[0] = $x
			$bestMove[1] = $y

			$bestScore = $sp
		EndIf
	Next
	ConsoleWrite("Zug ..." & @CRLF)
	If $DEBUG Then _Debug('_MoveComputer', ' Best Computer Move: >X:' & $bestMove[0] & " >Y: " & $bestMove[1], @ScriptLineNumber, "+")

	Return $bestMove
EndFunc   ;==>_MoveComputer

Func _PointsShow()
	$scores = _BoardScore($board)
	ConsoleWrite(@CRLF & "------ >> Punkte << ------" & @CRLF)
	If $PlayerCoin = "X" Then
		ConsoleWrite(">> Deine: " & $scores[0] & @CRLF)
		ConsoleWrite(">> Computer: " & $scores[1] & @CRLF)
	Else
		ConsoleWrite(">> Deine: " & $scores[1] & @CRLF)
		ConsoleWrite(">> Computer: " & $scores[0] & @CRLF)
	EndIf
	ConsoleWrite("--------------------------" & @CRLF & @CRLF)
EndFunc   ;==>_PointsShow

Func _2DArrayShuffle(ByRef $aArray)
	Local $tmp[2]
	$n = UBound($aArray, 1) - 1

	; Maybe make it more simple!
	While $n > 1
		$k = Random(0, $n, 1)
		$tmp[0] = $aArray[$n][0]
		$tmp[1] = $aArray[$n][1]
		$aArray[$n][0] = $aArray[$k][0]
		$aArray[$n][1] = $aArray[$k][1]
		$aArray[$k][0] = $tmp[0]
		$aArray[$k][1] = $tmp[1]
		$n -= 1
	WEnd
EndFunc   ;==>_2DArrayShuffle

Func _Exit()
	Exit
EndFunc   ;==>_Exit

Func _Debug($func, $text, $linenr, $mode)
	ConsoleWrite($mode & '> #' & $linenr & '> Debug - Function "' & $func & ": " & $text & @CRLF)
EndFunc   ;==>_Debug