Tutorial Project. Release 3

From wiki.visual-prolog.com

Written by Victor Yukhtenko. Email: victor@pdc.spb.su

The programs listed below are part of the Evolutions Tutorial Project article.

Game Rules ...

Goal

To demonstrate the features of the Visual Prolog programming language and system features:

  • data exchange principles in classes and objects;
  • advanced exception handling.

Functionality

  • the size of the field is 6x6 cells;
  • it may be more then two players;
  • the type of each player (human of computer) may be chosen;
  • the move may be very unfortunate if the computer can not find the successful move.

Code

  • Tested with Visual Prolog 7.3 build 7302.
  • Each Class plays the special role, like:
    • game – is the main application monitor;
    • human – the model of the player-human;
    • computer - the model of the player-computer;
    • seniourJudge – the Senior Judge, give the permission to the next player to make the move, waits the information regarding the end of the game and announces the winner;
    • juniourJudge – the Junior Judge, receives the move from the player, checks the validation of the moves, fixes the move of the player, which wins;
    • humanInterface – represents the information regarding the game flow to the observer and interprets the observer’s activities;
  • Due to an inefficient algorithm for the computer game field size more than 36 cells (6x6, for example) to install not recommended.

Install

To get the full set of projects of the series of the Polyline projects, please download archives using links at PDC forum:

  • VipSpbSDK_PE_73_Examples_Polyline_1_14.zip
  • VipSpbSDK_PE_73_Tools_Polyline_1_14.zip]

to any convenient directory. The directory named VipSpbSDK will be created automatically.

If you have the VipSpbSDK installed, then please open project Examples\Polyline\Polyline3\Polyline3.prj.

If you do not have the full set of examples from the VipSpbSDK, then please do as follows:

  • Create the new project using the interface strategy console
  • Build the project
  • Replace the content of the file "main.pro" by the code proposed below.
  • Build the project again. While building the project please respond “Add All”, when there will be the proposal to include additional packages to the project.

Run

Run the application using the “E” of the IDE or run the executable at the directory EXE.

/*****************************************************************************
Copyright (c) Victor Yukhtenko
 
SpbSolutions/Examples/Polyline
 
Predicates, which represent the strategy
    neighbour_nd,
    neighbourOutOfPolyLine_nd,
    resolveStep,
    successfulStep,
    stepCandidate
based on solutions proposed by Elena Efimova
********************************************/
goal
    mainExe::run(main::run).
 
/************
Class Main 
************/    
implement main
    open core
 
constants
    className = "main".
    classVersion = "1.0".
 
clauses
    classInfo(className, classVersion).
 
clauses
    run():-
        console::init(),   
        game::run().
 
end implement main
 
class game
open core
 
predicates
    classInfo : core::classInfo.
 
predicates
    run:().
end class game
 
implement game
open core
 
constants
    className = "game".
    classVersion = "1.0".
 
clauses
    classInfo(className, classVersion).
 
class facts
    playerNo_V:positive:=1.
 
clauses
    run():-
        humanInterface::announceStartUp(),
        PlayerType=humanInterface::getInput(humanInterface::playerType_S,toString(playerNo_V)),
        not(PlayerType=""),
        try
            Player=createPlayerObject(toTerm(PlayerType)),
            seniourJudge::addPlayer(Player),
            playerNo_V:=playerNo_V+1
        catch _TraceID do
            humanInterface::announceManagerError(humanInterface::errorPlayerType_S)
        end try,
        !,
        run().
    run():-
        startingPlayer().
 
class predicates
    playListMember:()->string nondeterm.
clauses
    playListMember()=PlayersListMember:-
        I = std::fromTo(1, list::length(seniourJudge::players_V)),
            Player=list::nth(I-1,seniourJudge::players_V),
            PlayersListMember=string::format("\n% - %",I,Player:name).
 
class predicates
    startingPlayer:().
clauses
    startingPlayer():-
        not(seniourJudge::players_V=[]) and not(seniourJudge::players_V=[_SiglePlayer]),
        findAll(PlayListMember,PlayListMember=playListMember(),PlayList),
        PlayListStr=string::concatList(PlayList),
        StartingPlayerStr=humanInterface::getInput(humanInterface::startingPlayer_S,PlayListStr),
        not(StartingPlayerStr=""), 
        try
            StartingPlayer=toTerm(StartingPlayerStr),
            !,
            startGame(StartingPlayer)
        catch _TraceID1 do
            humanInterface::announceManagerError(humanInterface::errorMustBeNumber_S),
            !,
            startingPlayer()
        end try.
    startingPlayer().
 
class predicates
    startGame:(positive).
clauses
    startGame(StartingPlayer):-
        try
            Player=list::nth(StartingPlayer-1,seniourJudge::players_V), % list::nth(...) needs unsigned
            !,
            seniourJudge::play(Player)
        catch _Trace_D do    
            humanInterface::announceManagerError(humanInterface::errorStartingPlayer_S),
            !
        end try,
        !,
        startingPlayer().
 
class predicates
    createPlayerObject:(positive)->player.
clauses
    createPlayerObject(1)=Player:-
        !,
        Player=human::new().
    createPlayerObject(2)=Player:-
        !,
        Player=computer::new().
    createPlayerObject(_)=_Player:-
        exception::raise(classInfo,wrongInputException,[]).
 
class predicates
    wrongInputException:exception.
clauses
    wrongInputException
        (
        classInfo,
        predicate_Name(),
        ""
        ).
 
end implement game
 
/************************
  Class seniorJudge
************************/
class seniourJudge
open core
 
properties
    players_V:player*.
    inProgress_V:boolean.
 
predicates
    play:(player CurrentPlayer).
    addPlayer:(player NextPlayer).
end class seniourJudge
 
implement seniourJudge
open core, humanInterface
 
class facts
    players_V:player*:=[].
    inProgress_V:boolean:=false.
 
clauses
    play(Player):-
        inProgress_V=false,
        humanInterface::showStage(),
        inProgress_V:=true,
        Player:move(),
        humanInterface::announceStarter(Player:name),
        play(Player),
        !.
    play(Player):-
        juniourJudge::isGameOver(),
        !,
        Player:announceWin(),
        foreach (Participant=list::getMember_nd(players_V) and not(Participant=Player)) do
            Participant:announceLoss()
        end foreach,
        inProgress_V:=false,
        juniourJudge::reset().
    play(Player):-
        NextPlayer=nextPlayer(Player),
        NextPlayer:move(),
        !,
        play(NextPlayer).
 
clauses
    addPlayer(NextPlayer):-
        players_V:=list::append(players_V,[NextPlayer]).
 
class predicates
    nextPlayer:(player CurrentPlayer)->player NextPlayer.
clauses
    nextPlayer(Player)=NextPlayer:-
        Index=list::tryGetIndex(Player,players_V),
        NextPlayer=list::tryGetNth(Index+1,players_V),
        !.
    nextPlayer(_Player)=list::nth(0,players_V).
 
end implement seniourJudge
 
/************************
  Class juniourJudge
************************/
class juniourJudge
open core
 
predicates
    classInfo : core::classInfo.
 
domains
    cell = c(positive,positive).
    stepType_D=
        ordinary_S;
        winner_S.
 
properties
    maxRow_P:positive.
    maxColumn_P:positive.
    polyline_P:cell*.
 
predicates
    neighbour_nd: (cell,cell) nondeterm (i,o) (i,i).
    neighbourOutOfPolyLine_nd:(cell,cell)->cell nondeterm.
    set: (string ).
    isGameOver:() determ.
    reset:().
 
end class juniourJudge
 
implement juniourJudge
open core, humanInterface
 
constants
    className = "JuniourJudge".
    classVersion = "1.0".
 
clauses
    classInfo(className, classVersion).
 
class facts
    maxRow_P:positive:=6.
    maxColumn_P:positive:=6.
    polyline_P:cell*:=[].
    endOfGame_V:boolean:=false.
 
clauses
    isGameOver():-
        endOfGame_V=true.
 
clauses
    set(InputString):-
        Cell=toTerm(InputString),
        handleInput (Cell).
 
clauses
    reset():-
        juniourJudge:: polyline_P:=[],
        juniourJudge::endOfGame_V:=false.
 
class predicates
    handleInput:(juniourJudge::cell).
clauses
    handleInput(Cell):-
        list::isMember(Cell,polyline_P),
        try 
            _=makePolyLine(Cell,polyline_P) % it will be an exception if wrong Cell, but we ignore it by failing
        catch _TraceID do
            fail
        end try,
        !,
        endOfGame_V:=true,
        humanInterface::showStep(Cell,winner_S).
    handleInput (Cell):-
        polyline_P:=makePolyLine(Cell,polyline_P),
        !,
        humanInterface::showStep(Cell,ordinary_S).
 
class predicates
    makePolyLine: (cell,cell*)-> cell* multi.
clauses
    makePolyLine(c(X,Y),[])=[c(X,Y)]:-
        X>0,X<=maxColumn_P,
        Y>0,Y<=maxRow_P,
        !.
    makePolyLine(NewCell,[SingleCell])=[NewCell,SingleCell]:-
        neighbour_nd(SingleCell, NewCell),
        !.
    makePolyLine(NewCell,[Left, RestrictingCell | PolyLineTail])=[NewCell, Left, RestrictingCell | PolyLineTail]:-
        NewCell=neighbourOutOfPolyLine_nd(Left,RestrictingCell).
    makePolyLine(NewCell,PolyLine)=list::reverse([NewCell,Left, RestrictingCell | PolyLineTail]):-
        [Left, RestrictingCell | PolyLineTail]= list::reverse(PolyLine),
        NewCell=neighbourOutOfPolyLine_nd(Left,RestrictingCell).
    makePolyLine(NewCell,_PolyLine)= _PolyLine1:-
        exception::raise(classInfo,wrongStepException,[namedValue("data",string(toString(NewCell)))]).
 
class predicates
    wrongStepException:exception.
clauses
    wrongStepException
        (
        classInfo,
        predicate_Name(),
        "the point % can not prolong the polyline!"
        ).
 
clauses
    neighbourOutOfPolyLine_nd(Cell,RestrictingCell)=NewCell:-
        neighbour_nd(Cell,NewCell),
            not(NewCell = RestrictingCell).
 
clauses
    neighbour_nd(c(X, Y), c(X + 1, Y)):- X < maxColumn_P.        
    neighbour_nd(c(X, Y), c(X, Y + 1)):- Y < maxrow_P.            
    neighbour_nd(c(X, Y), c(X - 1, Y)):- X > 1.
    neighbour_nd(c(X, Y), c(X, Y - 1)):- Y > 1.    
 
end implement juniourJudge
 
/******************************************
  Interface Player
******************************************/
interface player
 
predicates
    move:().
    announceWin:().
    announceLoss:().
 
properties
    name:string.
 
end interface player
 
/******************************************
  Class human
******************************************/
class human:player
open core
end class human
 
implement human
open core
 
facts
    name:string:=string::format("Hum_%",toString(This)).
 
clauses
    new():-
        Name=humanInterface::getInput(humanInterface::playerName_S,name),
        if not(Name="") then
            name:=Name
        end if.
 
clauses
    move():-
        InputString=humanInterface::getInput(humanInterface::playerStep_S),
        try
            juniourJudge::set(InputString)
        catch TraceID do
            handleException(TraceID),
            fail
        end try,
        !.
    move():-
        move().
 
clauses
    announceWin():-
        humanInterface::announceWin(name).
 
    announceLoss():-
        humanInterface::announceLoss(name).
 
 
class predicates
    handleException:(exception::traceId TraceID).
clauses
    handleException(TraceID):-
        foreach Descriptor=exception::getDescriptor_nd(TraceID) do
            Descriptor = exception::descriptor(
                _ClassInfo1,
                exception::exceptionDescriptor(_ClassInfo2,_PredicateName,Description),
                _Kind,
                ExtraInfo,
                _GMTTime,
                _ExceptionDescription,
                _ThreadId),
            if
                ExtraInfo=[namedValue("data",string(CellPointer))]
            then
                ErrorMsg=string::format(Description,CellPointer),
                humanInterface::announceError(ErrorMsg)
            else
                humanInterface::announceError("")
            end if
        end foreach.
 
end implement human
 
/******************************************
  Class computer
******************************************/
class computer:player
open core
end class computer
 
implement computer
open core
 
facts
    name:string:=string::format("Cmp_%",toString(This)).
 
clauses
    new():-
        Name=humanInterface::getInput(humanInterface::playerName_S,name),
        if not(Name="") then
            name:=Name
        end if.
 
clauses
    move():-
        humanInterface::announceThinker(name),
        Cell=resolveStep(),
        juniourJudge::set(toString(Cell)).
 
predicates
    resolveStep:()->juniourJudge::cell.
clauses
    resolveStep()=Cell:-
        Cell=successfulStep(juniourJudge::polyline_P),
        !.
    resolveStep()=Cell:-
        stepCandidate(juniourJudge::polyline_P,_PolyLine,Cell),
        !.
    resolveStep()=juniourJudge::c(X+1,Y+1):-
        X=math::random(juniourJudge::maxColumn_P-1),
        Y=math::random(juniourJudge::maxRow_P-1).
 
class predicates
    successfulStep: (juniourJudge::cell*)->juniourJudge::cell nondeterm.
clauses
    successfulStep(PolyLine)=Cell:-
        stepCandidate(PolyLine,_PolyLine1,Cell),
            list::isMember(Cell, PolyLine),
            !.
    successfulStep(PolyLine)=Cell:-
        stepCandidate(PolyLine, PolyLine1, Cell),
            not(_=successfulStep(PolyLine1)).
 
class predicates
    stepCandidate: (juniourJudge::cell*,juniourJudge::cell* [out],juniourJudge::cell [out]) nondeterm.
clauses
    stepCandidate([Cell],[Cell,NewCell], NewCell):-
        juniourJudge::neighbour_nd(Cell, NewCell).
    stepCandidate([Left, RestrictingCell | PolyLine],[NewCell,Left, RestrictingCell| PolyLine], NewCell):-
        NewCell=juniourJudge::neighbourOutOfPolyLine_nd(Left,RestrictingCell).
    stepCandidate(PolyLine,list::reverse([NewCell,Left, RestrictingCell |PolyLineTail]),NewCell):-
        [Left, RestrictingCell |PolyLineTail] = list::reverse(PolyLine),
        NewCell=juniourJudge::neighbourOutOfPolyLine_nd(Left,RestrictingCell).
 
clauses
    announceWin():-
        humanInterface::announceWin(name).
 
clauses
    announceLoss():-
        humanInterface::announceLoss(name).
 
end implement computer
 
/******************************************
  Class HumanInterface
******************************************/
class humanInterface
open core
 
predicates
    announceStartUp:().
 
predicates
    showStage:().
    showStep:(juniourJudge::cell,juniourJudge::stepType_D).
    getInput:(inputType_D,string StringParameter)->string InputString.
    getInput:(inputType_D)->string InputString.
 
predicates
    announceManagerError:(choiceError_D).
 
predicates
    announceStarter:(string Name).
    announceThinker:(string Name).
    announceWin:(string Name).
    announceLoss:(string Name).
    announceError:(string Description).
    announceError:().
 
domains
    choiceError_D=
        errorPlayerType_S;
        errorMustBeNumber_S;
        errorstartingPlayer_S.
 
domains
    inputType_D=
        playerStep_S;
        playerType_S;
        playerName_S;
        startingPlayer_S.
 
end class humanInterface
 
implement humanInterface
open core
 
constants
    cellMarkedOrdinary_C="*".
    cellMarkedWinner_C="O".
 
constants % messages
    thinker_C="% is thinking ...".
    beginner_C="First move done by: %".
    error_C="Error, % ".
    congratulation_C="Player % won!".
    sorryLoss_C="%,  Sorry, you loss :-(".
 
    playerStep_C="Please enter your move as c(X,Y): ".
    playerType_C="\nPlayer #%s. Please enter the player type (1-human, 2-computer, Enter - end of choice):".
    playerName_C="\nPlease assign the name to the player (% proposed):".
    startingPlayer_C="\nWho moves the first (PlayerNo or Enter - end of the game)?:".
 
    errorPlayerType_C="\nNo such player type exists! Enter - repeat input:".
    errorMustBeNumber_C="\nMust be number! Please repeat input:".
    errorstartingPlayer_C="\nNo such Player exiasts! Please repeat input:".
 
constants
    verticalSpace_C=2.
    horizontalSpace_C=3.
    emptyLineLenght_C=80.
 
constants % Position of Line
    starterLine_C=1.
    announceLine_C=starterLine_C+1.
    actionLine_C=announceLine_C+1.
    polylineLine_C=actionLine_C+1.
 
clauses
    getInput(InputType)=Input:-
        Input=getInput(InputType,"").
 
    getInput(InputType,StringParameter)=Input:-
        inputInvitation(InputType,StringParameter),
        Input = console::readLine(),
        console::clearInput().
 
class predicates
    inputInvitation:(inputType_D,string StringParameter).
clauses
    inputInvitation(playerStep_S,_StringParameter):-
        clearMessageArea(actionLine_C),
        writeMessage(actionLine_C,"%",playerStep_C).
    inputInvitation(playerName_S,StringParameter):-
        console::writef(playerName_C,StringParameter).
    inputInvitation(playerType_S,StringParameter):-
        console::writef(playerType_C,StringParameter).
    inputInvitation(startingPlayer_S,StringParameter):-
        console::write(StringParameter,startingPlayer_C).
 
clauses
    showStage():-
        console::clearOutput(),
        foreach I = std::fromTo(1, juniourJudge::maxColumn_P) do
            console::setLocation(console_native::coord(horizontalSpace_C*I, 0)), 
            console::write(I)
        end foreach,
        foreach J = std::fromTo(1, juniourJudge::maxRow_P) do    
            console::setLocation(console_native::coord(0, verticalSpace_C*J)), 
            console::write(J)
        end foreach.
 
clauses
    showStep(juniourJudge::c(X,Y),_Type):-
        console::setLocation(console_native::coord(horizontalSpace_C*X, verticalSpace_C*Y)),
        fail.
    showStep(_Cell,juniourJudge::ordinary_S):-
        console::write(cellMarkedOrdinary_C).
    showStep(_Cell,juniourJudge::winner_S):-
        console::write(cellMarkedWinner_C).
 
clauses
    announceStartUp():-
        console::clearOutput().
 
clauses
    announceStarter(Name):-
        clearMessageArea(starterLine_C),
        writeMessage(starterLine_C,beginner_C,Name).
 
clauses
    announceManagerError(errorPlayerType_S):-
        console::write(errorPlayerType_C),
        _=console::readLine().
    announceManagerError(errorMustBeNumber_S):-
        console::write(errorMustBeNumber_C).
    announceManagerError(errorstartingPlayer_S):-
        console::write(errorstartingPlayer_C).
 
clauses
    announceError():-
        announceError("").
 
    announceError(ErrorText):-
        clearMessageArea(announceLine_C),
        writeMessage(announceLine_C,error_C,ErrorText).
 
clauses
    announceWin(Name):-
        clearMessageArea(announceLine_C),
        writeMessage(announceLine_C,congratulation_C,Name),
        showPolyLine(),
        _ = console::readLine().
 
    announceLoss(Name):-
        clearMessageArea(announceLine_C),
        writeMessage(announceLine_C,sorryLoss_C,Name),
        showPolyLine(),
        _ = console::readLine().
 
clauses
    announceThinker(Name):-
        clearMessageArea(announceLine_C),
        clearMessageArea(actionLine_C),
        writeMessage(actionLine_C,thinker_C,Name).
 
class predicates
    showPolyLine:().
clauses
    showPolyLine():-
        clearMessageArea(actionLine_C),
        writeMessage(polylineLine_C,"%",toString(juniourJudge::polyline_P)).
 
class predicates
    clearMessageArea:(positive AreaID).
clauses
    clearMessageArea(AreaID):-
        console::setLocation(console_native::coord(0,juniourJudge::maxRow_P*verticalSpace_C+AreaID)),
        console::write(string::create(emptyLineLenght_C," ")).
 
class predicates
    writeMessage:(positive AreaID,string FormatString,string ParameterString).
clauses
    writeMessage(AreaID,FormatString,ParameterString):-
        console::setLocation(console_native::coord(0, juniourJudge::maxRow_P*verticalSpace_C+AreaID)),
        console::writef(FormatString,ParameterString).
 
end implement humanInterface