%==============================================================================
% Project:	Course "Logic Programming and Deductive Databases"
% Version:	Summer 2021, University of Halle
% Module:	connect4.pl
% Purpose:	Manges "Connect Four" Match
% Last Change:	08.07.2021
% Language:	Prolog (Tested with SWI Prolog)
% Authors:	Stefan Brass
% Email:	brass@informatik.uni-halle.de
% Address:	Universitaet Halle, Inst. f. Informatik, D-06099 Halle, Germany
% Copyright:	(c) 2021 by Stefan Brass, Parts date back to 1992
% Copying:	You may do what you want with this, but you cannot make me
%       	responsible for anything. Please mark changed versions.
%==============================================================================

:- module(connect4, [run/0]).

:- use_module(gamestate,
		[init_state/1, move/4, won/2, board_full/1, print/1, symbol/2]).

% Algorithm for Yellow:
%:- use_module(human,
:- use_module(trivial,
		[init_yellow/1, choose_move_yellow/3, opponent_move_red/3]).

% Algorithm for Red:
%:- use_module(human,
:- use_module(trivial,
		[init_red/1, choose_move_red/3, opponent_move_yellow/3]).

%==============================================================================
% Run a match:
%==============================================================================

run :-
	write('Willkommen zum "Vier gewinnt" Spiel'),
	nl,
	nl,
	init_state(GameState),
	init_yellow(YellowState),
	init_red(RedState),
	!,
	% Yellow starts:
	run(yellow, GameState, YellowState, RedState).

%==============================================================================
% Move of one colour:
%==============================================================================

%------------------------------------------------------------------------------
% run(+Player, +GameState, +PlayerState, +OpponentState):
%------------------------------------------------------------------------------

% Continue match:

run(Player, GameState, PlayerState, OpponentState) :-
	print(GameState),
	player_name(Player, PlayerName),
	symbol(Player, PlayerSymbol),
	write(PlayerName),
	write(' ('),
	write(PlayerSymbol),
	write(') ist am Zug ...'),
	nl,
	check_choose_move(Player, PlayerState, Col, NewPlayerState),
	write(PlayerName),
	write(' zieht in Spalte '),
	write(Col),
	write('.'),
	nl,
	check_move(Player, GameState, Col, NewGameState),
	check_opponent_move(Player, OpponentState, Col, NewOpponentState),
	!,
	cond_continue(Player, NewGameState, NewPlayerState, NewOpponentState).

%------------------------------------------------------------------------------
% player_name(+Player, -PlayerName):
%------------------------------------------------------------------------------

player_name(yellow, 'Gelb').
player_name(red,    'Rot').

%------------------------------------------------------------------------------
% opponent(+Player, -Opponent):
%------------------------------------------------------------------------------

opponent(yellow, red).
opponent(red,    yellow).

%------------------------------------------------------------------------------
% check_choose_move(+Player, +PlayerState, -Col, -NewPlayerState):
%------------------------------------------------------------------------------

% This calls choose_move_yellow/choose_move_red and handles failure.

check_choose_move(yellow, YellowState, Col, NewYellowState) :-
	choose_move_yellow(YellowState, Col, NewYellowState),
	!.

check_choose_move(red, RedState, Col, NewRedState) :-
	choose_move_red(RedState, Col, NewRedState),
	!.

check_choose_move(Player, _, _, _) :-
	nl,
	player_name(Player, PlayerName),
	write('*** choose_move_'),
	write(Player),
	write(' ist fehlgeschlagen. '),
	write(PlayerName),
	write(' gibt auf. ***'),
	nl,
	fail.

%------------------------------------------------------------------------------
% check_move(+Player, +GameState, +Col, -NewGameState):
%------------------------------------------------------------------------------

check_move(Player, GameState, Col, NewGameState) :-
	move(GameState, Player, Col, NewGameState),
	!.

check_move(Player, _, Col, _) :-
	nl,
	player_name(Player, PlayerName),
	write('*** Zug in Spalte '),
	write(Col),
	write(' ist ungueltig. '),
	write(PlayerName),
	write(' ist disqualifiziert. ***'),
	nl,
	fail.

%------------------------------------------------------------------------------
% check_opponent_move(+Player, +RedState, -YellowCol, -NewRedState):
%------------------------------------------------------------------------------

% This calls opponent_move_yellow/opponent_move_red and handles failure.

check_opponent_move(yellow, OpponentState, Col, NewOpponentState) :-
	opponent_move_yellow(OpponentState, Col, NewOpponentState),
	!.

check_opponent_move(red, OpponentState, Col, NewOpponentState) :-
	opponent_move_red(OpponentState, Col, NewOpponentState),
	!.

check_opponent_move(Player, _, _, _) :-
	nl,
	opponent(Player, Opponent),
	player_name(Opponent, OpponentName),
	write('*** opponent_move_'),
	write(player),
	write(' ist fehlgeschlagen. '),
	write(OpponentName),
	write(' ist disqualifiziert. ***'),
	nl,
	fail.

%------------------------------------------------------------------------------
% cond_continue(+Player, +GameState, +PlayerState, +OpponentState):
%------------------------------------------------------------------------------

% Check whether game has ended after a move. If not, continue with opponent.

cond_continue(Player, GameState, _PlayerState, _OpponentState) :-
	won(GameState, Player),
	!,
	print(GameState),
	player_name(Player, PlayerName),
	write('*** '),
	write(PlayerName),
	write(' hat gewonnen ***'),
	nl.

cond_continue(_Player, GameState, _PlayerState, _OpponentState) :-
	board_full(GameState),
	!,
	print(GameState),
	write('*** Das Spiel endet unentschieden ***'),
	nl.

cond_continue(Player, GameState, PlayerState, OpponentState) :-
	opponent(Player, Opponent),
	run(Opponent, GameState, OpponentState, PlayerState).
	% Note that OpponentState and PlayerState are inversed,
	% because now the opponent get's it's turn as player.

