Pracownia 2008

Poniższe polecenie kompiluje program w Haskellu, a nastepnie wyświetla jego wydajność:

ghc -prof -auto-all --make zad.hs -o zad && cat input | ./zad +RTS -p -RTS && cat zad.prof

Zadanie 1

/*
 * Usage:
 * Format: M = [ (n1,v1), ..., (nn, vn) ]
 * n - count (number)
 * v - amount (value)
 * Coin counts may not be negative
 *
 * ?- payment(123, [(10,1),(10,2),(10,5),(10,10)], X).
 * ?- payment(123, [(10,1),(10,2),(10,5),(10,10)], [(10,1),(9,2),(9,5),(5,10)]).
 */

% We can pay 0 with an empty list of coins.
payment(0, _, []) :-
        !.                                      % nothing else we can do

% 0 coins of some amount.
payment(Amount, [(0,_)|Avail], Result) :- 
        !,                                      % nothing more we can do
        payment(Amount, Avail, Result).         % dispose of them
                                
% try paying with Num coins of type Coin
payment(Amount, [(Num,Coin)|Avail], [(Num,Coin)|Result]) :- 
        Amount > 0,             
        NAmount is Amount - Num * Coin,
        payment(NAmount, Avail, Result).
                                
% decrease the Num of coins of type Coin by one
payment(Amount, [(Num,Coin)|Avail], Result) :- 
        Amount > 0,             
        NNum is Num - 1,                        % the second predicate compares NNum against 0
        payment(Amount, [(NNum,Coin)|Avail], Result).

Zadanie 2

/*                              
 * Usage:                       
 * ?- queens(5,X).              
 */                             
                                
% Check the newly added element for conflict with existing ones
check_prev([], _, _, _) :- !.   
check_prev([Wj|List], J, I, Wi) :-
        Wj =\= Wi,              
        Wj - Wi =\= I-J,        
        Wj - Wi =\= J-I,        
        Jn is J + 1,
        check_prev(List, Jn, I, Wi).

% Gnu Prolog lacks the "between" builtin predicate.
% Assigns Nextnum a number from range Curnum..Maxnum inclusive

% Return the number unchanged
assignnum(Curnum, Maxnum, Curnum) :-
        Curnum =< Maxnum.                                       % If not out of bounds

% Increment the number
assignnum(Curnum, Maxnum, Nextnum) :-
        Curnum =< Maxnum,                                       % If not out of bounds
        Nnum is Curnum + 1,
        assignnum(Nnum, Maxnum, Nextnum).

% A wrapper is needed
queens(N,L) :- queens(0,N,[],L, N).

% Arguments are as follows: Number_of_existing_elements, Max_elements, Accumulator, Result, Position_of_the_new_element

% Enough elements have been generated
queens(Height, Height, L, L, _) :- !.

% Add a new element
queens(Width, Height, Acc, L, Last) :-
        NewWidth is Width + 1,
        assignnum(1, Height, Num),                              % Assign Num a value from 1 to Height inclusive
                                                                                                                % Gprolog appears to lack the between predicate
        NewLast is Last - 1,
        check_prev(Acc, Last, NewLast, Num),                    % Check Num against previous elements
        queens(NewWidth, Height, [Num|Acc], L, NewLast).        % Check successiful, continue

Zadanie 3

/*
 * Usage:
 * Minimum valid board size is 1.
 * Board sizes 2, 3, 4 produce no valid knight paths.
 *
 * ?- knight_path(5, X).
 */

% Available moves, out-of-bounds checks reduced to a minimum
nextmove((FromX, FromY), (ToX, ToY), Max) :- ToX is FromX+1, ToX =< Max,        ToY is FromY+2, ToY =< Max.
nextmove((FromX, FromY), (ToX, ToY), Max) :- ToX is FromX+2, ToX =< Max,        ToY is FromY+1, ToY =< Max.
nextmove((FromX, FromY), (ToX, ToY), Max) :- ToX is FromX+2, ToX =< Max,        ToY is FromY-1, ToY > 0.
nextmove((FromX, FromY), (ToX, ToY), Max) :- ToX is FromX+1, ToX =< Max,        ToY is FromY-2, ToY > 0.

nextmove((FromX, FromY), (ToX, ToY), _Mx) :- ToX is FromX-1, ToX > 0,           ToY is FromY-2, ToY > 0.
nextmove((FromX, FromY), (ToX, ToY), _Mx) :- ToX is FromX-2, ToX > 0,           ToY is FromY-1, ToY > 0.
nextmove((FromX, FromY), (ToX, ToY), Max) :- ToX is FromX-2, ToX > 0,           ToY is FromY+1, ToY =< Max.
nextmove((FromX, FromY), (ToX, ToY), Max) :- ToX is FromX-1, ToX > 0,           ToY is FromY+2, ToY =< Max.

% A wrapper is required
knight_path(Max, Path) :-
        Moves is Max*Max,
        knight_path(Max, Moves, [(1,1)], Path).

% Arguments are as follow: Map_width, Total_number_of_moves, History_of_moves/Accumulator, Result

% Max*Max moves done
knight_path(_Max, 1, Path, Path) :-
        !.

knight_path(Max, Moveno, [(X,Y)|T], Path) :- 
        nextmove((X,Y),(Xn,Yn),Max),                            % get next potential move
        \+ memberchk((Xn,Yn), T),                               % if not already done
        NMoveno is Moveno - 1,
        knight_path(Max, NMoveno, [(Xn,Yn),(X,Y)|T], Path).     % perform the move

Zadanie 4

/*
 * Usage:
 * ?- solve(4, 6, 6, [(4,4),(-1,3),(-6,2),(4,2),(-3,1),(1,1),(4,0),(-5,-1),(-1,-1),(1,-1),(6,-2),(1,-3),(-3,-3)], W).
 */

% Main predicate
solve(MapSize, NoOfIslands, IslandSize, BlackenedIslands, Result ) :-
        NumOfHex is (MapSize*2+1)+2*((MapSize*2+(MapSize+1))*MapSize//2),       % Map size in hexagons
        NumToBlacken is NumOfHex - IslandSize*NoOfIslands,                      % How many hexagons to blacken
        generate_map( MapSize, Map ),                                           % List of available hexagons
        set_diff( Map, BlackenedIslands, NMap ),                                % Remove blackened islands      
        dosolve( MapSize, NMap, NumToBlacken, IslandSize, NoOfIslands, [], BlackenedIslands, Result ).

% Zero more islands to build, end.
dosolve( _MapSize, Map, NumToBlacken, _IslandSize, 0, _Island, BlackenedIslands, Result ) :-
        !,                                                                      % This is the only valid predicate
        append(Map, BlackenedIslands, Result),                                  % Blacken what is left of the map
        length(Result, RLen),
        RLen = NumToBlacken.                                                    % Check if the length of black hexagons is correct

% Island is empty, add to the island
dosolve( MapSize, [Elem|Map], NumToBlacken, IslandSize, NoOfIslands, [], BlackenedIslands, Result ) :-
        dosolve( MapSize, Map, NumToBlacken, IslandSize, NoOfIslands, [Elem], BlackenedIslands, Result ).

% Island is empty, add to blacked
dosolve( MapSize, [Elem|Map], NumToBlacken, IslandSize, NoOfIslands, [], BlackenedIslands, Result ) :-
        !,                                                                      % No more empty-island predicates
        NBlackenedIslands = [Elem|BlackenedIslands],
        length(NBlackenedIslands, NBLen),
        NBLen =< NumToBlacken,                                                  % Check if the length of black hexagons is correct
        dosolve( MapSize, Map, NumToBlacken, IslandSize, NoOfIslands, [], NBlackenedIslands, Result ).

% Island is full
dosolve( MapSize, Map, NumToBlacken, IslandSize, NoOfIslands, Island, BlackenedIslands, Result ) :-
        length(Island, IslandLength),
        IslandLength = IslandSize,                                              % Island is full
        !,                                                                      % No more full-island predicates

        border( MapSize, Island, Border ),                                      % Get the islands border
        set_and( Map, Border, NBorder ),
        set_sum( NBorder, BlackenedIslands, NBlackenedIslands ), 
        set_diff( Map, NBorder, NMap ),                                         % Blacken those elements of the border that
                                                                                % belong to the map, and delete them from the map
        length(NBlackenedIslands, NBLength),
        NBLength =< NumToBlacken,                                               % Check if the length of black hexagons is correct
        NNoOfIslands is NoOfIslands - 1,                                        % Decrement the number of islands
        dosolve( MapSize, NMap, NumToBlacken, IslandSize, NNoOfIslands, [], NBlackenedIslands, Result ).

% Island not empty and not full, add to the island
dosolve( MapSize, Map, NumToBlacken, IslandSize, NoOfIslands, Island, BlackenedIslands, Result ) :-
        getadjacent( MapSize, Map, Island, Island, Adjacent ),  % Get some adjacent hexagon (if it exists)
        delete( Map, Adjacent, NMap ),                                          % Remove it from the map
        dosolve( MapSize, NMap, NumToBlacken, IslandSize, NoOfIslands, [Adjacent|Island], BlackenedIslands, Result ).

% Island not empty and not full, add to blacked
dosolve( MapSize, Map, NumToBlacken, IslandSize, NoOfIslands, Island, BlackenedIslands, Result ) :-
        getadjacent( MapSize, Map, Island, Island, Adjacent ),  % Get some adjacent hexagon (if it exists)
        NBlackenedIslands = [Adjacent|BlackenedIslands],                % Blacken it
        length(NBlackenedIslands, NBLen),
        NBLen =< NumToBlacken,                                                  % Check if the length of black hexagons is correct
        delete( Map, Adjacent, NMap ),                                          % Remove it from the map
        dosolve( MapSize, NMap, NumToBlacken, IslandSize, NoOfIslands, Island, NBlackenedIslands, Result ).

%%% BEGIN %%% GENERATE MAP

% Generate the map. Predicate run only once per problem.
generate_map( MapSize, Map ) :-
        X is -2*MapSize,
        generate_x( MapSize, [], Map, X ).

generate_x( MapSize, Acc, Map, X ) :-
        X =< 2*MapSize, !,
        MinusMapSize is -MapSize,
        generate_y( MapSize, Acc, NAcc, X, MinusMapSize ),
        Xn is X + 1,
        generate_x( MapSize, NAcc, Map, Xn ).
generate_x( _MapSize, Acc, Acc, _).

generate_y( MapSize, Acc, Map, X, Y ) :-
        Y =< 2*MapSize,
        Mod is ((X + Y) mod 2),
        Mod = 0,
        inbounds( MapSize, (X,Y) ), !,
        Yn is Y + 1,
        generate_y( MapSize, [(X,Y)|Acc], Map, X, Yn).

% Either not Mod == 0 or not in bounds
generate_y( MapSize, Acc, Map, X, Y ) :-
        Y =< 2*MapSize, !,
        Yn is Y + 1,
        generate_y( MapSize, Acc, Map, X, Yn).

% Y > 2*MapSize
generate_y( _, Acc, Acc, _, _).


%%%% END %%%% GENERATE MAP

%%% BEGIN %%% NEIGHBOURHOOD-RELATED PREDICATES

% Check to see if (X,Y) is on the map
inbounds( MapSize, (X,Y) ) :-
        abs(X, Xabs),
        abs(Y, Yabs),
        Yabs =< MapSize,
        Yabs + Xabs =< 2 * MapSize.

% Checks for adjacency
% Without the fourth argument, returns succesive neighbours
adjacent( MapSize, Elem, NElem ) :-
        adjacent( MapSize, Elem, NElem, _).                          % Wrapper

% With the fourth argument, returns a neighbour that lies in the direction N ([1..6])
adjacent( MapSize, (X,Y), (Xn,Yn), N) :-
        adjacent_pair(N, (Xa,Ya)),                                   % Get new potential neighbour vector
        Xn is X + Xa,
        Yn is Y + Ya,
        inbounds(MapSize, (Xn,Yn)).                                  % Check, if the new element is in bounds

% Assemble a list of all valid neighbours
adjacent_list( MapSize, Elem, List ) :-
        adjacent_list( MapSize, Elem, List, 6).

% The fourth argument is the direction. 
adjacent_list( _, _, [], 0 ) :- !.

% A neighbour in the direction N exists:
adjacent_list( MapSize, Elem, [NElem|List], N) :-
        adjacent( MapSize, Elem, NElem, N), !,
        Nn is N - 1,
        adjacent_list( MapSize, Elem, List, Nn).

% A neighbour in the direction N doesn't exist:
adjacent_list( MapSize, Elem, List, N) :-
        Nn is N - 1,
        adjacent_list( MapSize, Elem, List, Nn).

% Hardcoded adjacency pairs
adjacent_pair(1, ( 2, 0)).
adjacent_pair(2, ( 1,-1)).
adjacent_pair(3, (-1,-1)).
adjacent_pair(4, (-2, 0)).
adjacent_pair(5, (-1, 1)).
adjacent_pair(6, ( 1, 1)).

% Get a border list of an island (All hexagons adjacent to all elements of the island, excluding the island itself)
border( MapSize, Island, Neighbhd ) :-
        border( MapSize, Island, [], Neighbhd_incl ),                % The ternary border predicate returns a neighbourhood
                                                                     % with the island included...
        set_diff( Neighbhd_incl, Island, Neighbhd ).                 % ...so it needs to be excluded.

border( _, [], Acc, Acc ) :- !.
border( MapSize, [P|Island], Acc, Neighbhd ) :-
        adjacent_list( MapSize, P, List ),                           % Get a list of all hexagons adjacent to an element
        set_sum( List, Acc, Sum ),                                   % For uniqueness
        border( MapSize, Island, Sum, Neighbhd ).

% Return an element adjacent to the island, that is on the map
getadjacent( MapSize, Map, WholeIsland, [Elem|_], Adj ) :-
        adjacent( MapSize, Elem, Adj ),                              % Get an element adjacent to some element of the island...
        \+ memberchk( Adj, WholeIsland),                             % ...that is not in the island...
        memberchk( Adj, Map ), !.                                    % ...but is on the map

% If that failed, check another element of the island for neighbours
getadjacent( MapSize, Map, WholeIsland, [_|Island], Adj ) :-
        getadjacent( MapSize, Map, WholeIsland, Island, Adj ).
        
%%%% END %%%% NEIGHBOURHOOD-RELATED PREDICATES

%%% BEGIN %%% MATHEMATICAL PREDICATES

% Absolute value of a number
abs(X, Y) :- X >= 0, !, Y is X.
abs(X, Y) :- Y is -X.

% Calculate a sum of two sets (Set1 OR Set2)
set_sum( Set1, Set2, Sum ) :-
        set_sum( Set1, Set2, Set2, Sum ).

set_sum( [], _, Acc, Acc ) :- !.
set_sum( [E|Set1], Set2, Acc, Sum ) :-
        memberchk( E, Set2 ), !,
        set_sum( Set1, Set2, Acc, Sum ).
set_sum( [E|Set1], Set2, Acc, Sum ) :-
        set_sum( Set1, Set2, [E|Acc], Sum ).

% Calculate an intersection of two sets (Set1 AND Set2)
set_and( Set1, Set2, And ) :-
        set_and( Set1, Set2, [], And ).

set_and( [], _, Acc, Acc ) :- !.
set_and( [E|Set1], Set2, Acc, And ) :-
        memberchk( E, Set2 ), !,
        set_and( Set1, Set2, [E|Acc], And ).
set_and( [_|Set1], Set2, Acc, And ) :-
        set_and( Set1, Set2, Acc, And ).

% Calculate a difference of two sets (Set1/Set2)
set_diff( Set1, Set2, Diff ) :-
        set_diff( Set1, Set2, [], Diff ).

set_diff( [], _, Acc, Acc ) :- !.
set_diff( [X|Set1], Set2, Acc, Diff ) :-
        memberchk( X, Set2 ), !,
        set_diff( Set1, Set2, Acc, Diff ).

set_diff( [X|Set1], Set2, Acc, Diff ) :-
        set_diff( Set1, Set2, [X|Acc], Diff ).

%%%% END %%%% MATHEMATICAL PREDICATES

Zadanie 5

SWI-Prolog

/*
 * Usage:
 * run("{write 2;}").
 * or
 * run_file('prog.imp').
 * |: 99.
 * Read requires it's input to be terminated by a comma ('.')
 * Only numbers can be entered.
 */

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% LEXER %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Convert the input string into a list of tokens. Invalid characters ($,&,...) produce an error.

lexer(Lo,[Token|Tokens])    -->
    white(Lo,L),                            % Dispose of white spaces
    (
        % Arithmetic operators
        "+",  !, { Token = tokAdd };
        "-",  !, { Token = tokSub };
        "/",  !, { Token = tokDiv };
        "*",  !, { Token = tokMul };
        "%",  !, { Token = tokMod };

        % Arithmetic relations
        "<=", !, { Token = tokLeq };
        "<",  !, { Token = tokLt };
        ">=", !, { Token = tokGeq };
        ">",  !, { Token = tokGt };
        "=",  !, { Token = tokAssgn };
        "!=", !, { Token = tokNeq };

        % Boolean relations
        "&&", !, { Token = tokAnd };
        "||", !, { Token = tokOr };
        "\!", !, { Token = tokNot };

        % Other operators
        ";",  !, { Token = tokSColon };
        ",",  !, { Token = tokComma };
        "(",  !, { Token = tokLParen };
        ")",  !, { Token = tokRParen };
        "[",  !, { Token = tokLSquare };
        "]",  !, { Token = tokRSquare };
        "{",  !, { Token = tokLCurly };
        "}",  !, { Token = tokRCurly };

        % Control flow, builtin functions, etc
        dnumf(Numf), !, lexnum(Numf, Number), { Token = tokNum(Number) };
        csymf(Symf), !, lexsym(Symf, Symbol), {
            % Symbol is either a reserved name...
            memberchk( (Symbol, Token), [   (if,    tokIf(L)),
                                            (else,  tokElse),
                                            (while, tokWhile(L)),
                                            (abort, tokAbort(L)),
                                            (var,   tokVar(L)),
                                            (read,  tokRead(L)),
                                            (write, tokWrite(L))]), !
            ; % OR
            % ...or a variable name
            Token = tokVariable(Symbol)
        };
        [X], { throw(errUnknowntoken(L,X)) }        % Unknown token error
    ), !,
    lexer(L,Tokens).

lexer(L,[])             --> white(L,_), !.
lexer(_,[])             --> [].

% Comments and whitespaces call each other to make constructs like this valid: "/*a*/  /*a*/  /*a*/"
% C-style comments
comment(L,Ln)           --> "/*", !, uncomment(L,Ln).
comment(L,L)            --> [].

uncomment(L,Ln)         --> "*/", !, white(L,Ln).
uncomment(L,Ln)         --> [Char], { code_type(Char, newline), Lnn is L + 1 }, !, uncomment(Lnn,Ln).
uncomment(L,Ln)         --> [_], uncomment(L,Ln).

% Eat up whitespaces
white(L,Ln)             --> [Char], { code_type(Char, newline), Lnn is L + 1 }, !, white(Lnn, Ln).
white(L,Ln)             --> [Char], { code_type(Char, space) }, !, white(L,Ln).
white(L,Ln)             --> comment(L,Ln), !.
white(L,L)              --> [].

% Valid c (imp) symbols start with a letter or an underscore and later consists of letters, digits or underscores
csymf(X)                --> [X], {code_type(X, csymf)}.
csym([X|Xs])            --> [X], {code_type(X, csym)}, !, csym(Xs).
csym([])                --> [].

% Read a string and convert it to a lexem
lexsym(First, Lex)      --> csym(Strlex), { atom_codes(Lex, [First|Strlex]) }.

% Decimal number
dnumf(X)                --> [X], { code_type(X, digit) }.
dnum([X|Xs])            --> [X], { code_type(X, digit) }, !, dnum(Xs).
dnum([])                --> [].

% Read a string and convert it to a number
lexnum(First, Num)      --> dnum(Strnum), {number_chars(Num, [First|Strnum])}.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% PARSER %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

:- op(900, xfy, :=).

% Program, block, empty
program([Ast])                      --> blok(Ast), !.
blok(block(Instrs))                 --> [tokLCurly], !, instructions(Instrs), [tokRCurly], !.
empty                               --> [].

% Instructions
instructions([Instr|Rest])          --> instruction(Instr), instructions(Rest), !.
instructions([])                    --> empty.

instruction(abort)                  --> [tokAbort(_)],  [tokSColon], !.
instruction(var(Instr))             --> [tokVar(_)],    declarators(Decle_rs), [tokSColon], {flatten(Decle_rs,Instr)},!.
instruction(Cell:=Expr)             --> cell(Cell),     [tokAssgn], expr(Expr), [tokSColon], !.
instruction(read(Cell))             --> [tokRead(_)],   cell(Cell), [tokSColon], !.
instruction(write(Expr))            --> [tokWrite(_)],  expr(Expr), [tokSColon], !.
instruction(if(Bool,Then,Else))     --> [tokIf(_)],     [tokLParen], expr(Bool), [tokRParen], instruction(Then),
                                        [tokElse], instruction(Else), !.
instruction(if(Bool,Then))          --> [tokIf(_)],     [tokLParen], expr(Bool), [tokRParen], instruction(Then), !.
instruction(while(Bool,Body))       --> [tokWhile(_)],  [tokLParen], expr(Bool), [tokRParen], instruction(Body), !.
instruction(Instr)                  --> blok(Instr), !.

% Errors
instruction(_)                      --> [tokAbort(L)],          { throw(errParsercf(L,abort)) }.
instruction(_)                      --> [tokVar(L)],            { throw(errParsercf(L,var)) }.
instruction(_)                      --> [tokRead(L)],           { throw(errParsercf(L,read)) }.
instruction(_)                      --> [tokWrite(L)],          { throw(errParsercf(L,write)) }.
instruction(_)                      --> [tokIf(L)],             { throw(errParsercf(L,if)) }.
instruction(_)                      --> [tokWhile(L)],          { throw(errParsercf(L,while)) }.
% Other errors are not reported in detail, and are thrown by the "run" predicate.

% Declarators
declarators([Decle_r|Rest])         --> declarator(Decle_r), [tokComma], !, declarators(Rest).
declarators([Decle_r])              --> declarator(Decle_r).

declarator((variable(Ide)=Expr))    --> [tokVariable(Ide)], [tokAssgn], expr(Expr).
declarator(array(Ide,[Expr]))       --> [tokVariable(Ide)], [tokLSquare], expr(Expr), [tokRSquare].
declarator(variable(Decle_r))       --> [tokVariable(Decle_r)].

% Cell, expr
cell(array(Ide,[Expr]))             --> [tokVariable(Ide)], [tokLSquare], !, expr(Expr), [tokRSquare].
cell(variable(Ide))                 --> [tokVariable(Ide)].

expr(or(E1,E2))                     --> disjunct(E1), [tokOr], !, expr(E2).
expr(Expr)                          --> disjunct(Expr).

% Disjunct, conjunct
disjunct(and(E1,E2))                --> conjunct(E1), [tokAnd], !, disjunct(E2).
disjunct(Expr)                      --> conjunct(Expr).

conjunct(Conjunct)                  --> arith_expr(LExpr), op_rel(=), !, arith_expr(RExpr),
                                        { Conjunct =.. [=, LExpr, RExpr] }.
conjunct(Conjunct)                  --> arith_expr(LExpr), op_rel(Op), !, arith_expr(RExpr),
                                        { Conjunct =.. [Op, LExpr, RExpr] }.
conjunct(Conjunct)                  --> arith_expr(Conjunct).

% Arithmetic expression, summand, factor. An accumulator is needed for correcness of 2-2-2 etc.
arith_expr(Expr)                    --> summand(Summand), arith_expr(Summand, Expr).
arith_expr(Acc, Expr)               --> op_add(Op), !, summand(Summand),
                                        { Acc1 =.. [Op, Acc, Summand] }, arith_expr(Acc1, Expr).
arith_expr(Acc, Acc)                --> [].

summand(Expr)                       --> factor(Factor), summand(Factor, Expr).
summand(Acc, Expr)                  --> op_mult(Op), !, factor(Factor),
                                        { Acc1 =.. [Op, Acc, Factor] }, summand(Acc1, Expr).
summand(Acc, Acc)                   --> [].

factor(not(Expr))                   --> [tokNot], factor(Expr).
factor(neg(Expr))                   --> [tokSub], factor(Expr).
factor(constant(N))                 --> [tokNum(N)].
factor(cell(Var))                   --> cell(variable(Var)).
factor(array(A,B))                  --> cell(array(A,B)).
factor(Expr)                        --> [tokLParen], expr(Expr), [tokRParen].

% Additive operators
op_add(+)               --> [tokAdd].
op_add(-)               --> [tokSub].

% Multiplicative operators
op_mult(*)              --> [tokMul].
op_mult(//)             --> [tokDiv].
op_mult(mod)            --> [tokMod].

% Arithmetic relations
op_rel(<)               --> [tokLt].
op_rel(=<)              --> [tokLeq].
op_rel(>)               --> [tokGt].
op_rel(>=)              --> [tokGeq].
op_rel(=)               --> [tokAssgn].
op_rel(=\=)             --> [tokNeq].

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% INTERPRETER %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Lookup a variable value (write x)
lookup(M, X, _)                         :- memberchk((_,X,$non), M), !, throw(errUninited(X)).  % Error, uninitialized
lookup(M, X, N)                         :- memberchk((_,X,N), M), !.
lookup(_, X, _)                         :- throw(errNonexistent(X)).                            % Error, nonexistent

% Lookup an array value (write x[7])
lookuparray([(_,array(X,L),V)|_],X,I,N) :- L >= I, I >= 0, memberchk( (I,N), V).
lookuparray([(_,array(X,L),_)|_],X,I,_) :- L >= I, I >= 0, !, throw(errUninited(X,I)).
lookuparray([(_,array(X,L),_)|_],X,I,_) :- !, throw(errOutofbounds(X,L,I)).                     % Error, out of bounds
lookuparray([_|M],X,I,N)                :- !, lookuparray(M,X,I,N).

% Create a new array (var x[17])
newarray([(D,X,N)|M], Xn, Nn, [(D,array(Xn,Nnn),[]),(D,X,N)|M])
                                        :- \+ memberchk((D,array(Xn,_),_), [(D,X,N)|M]), Nn > 0, !, Nnn is Nn-1.
newarray([(D,X,N)|M], Xn, Nn, _)        :- \+ memberchk((D,array(Xn,_),_), [(D,X,N)|M]), throw(errArraytosmall(Xn,Nn)).
newarray([(D,_,_)|_], Xn, _, _)         :- throw(errRedeclr(Xn,D)).                             % Error, redeclared

% Create a new variable (var x)
newvar([(D,X,N)|M], Xn, Nn, [(D,Xn,Nn),(D,X,N)|M])  :- \+ memberchk((D,Xn,_), [(D,X,N)|M]), !.
newvar([(D,_,_)|_], Xn, _, _)                       :- throw(errRedeclr(Xn,D)).                 % Error, redeclared

% Update an array element (x[7] = 1)
updatearrayval([], I, N, [(I,N)])                   :- !.
updatearrayval([(I,_)|V], I, N, [(I,N)|V])          :- !.
updatearrayval([E|V], I, N, [E|Vn])                 :- updatearrayval(V, I, N, Vn).

updatearray([(D,array(X,L),V)|T], X, I, N, [(D,array(X,L),Vn)|T])   :- L >= I, I >= 0, !, updatearrayval(V, I, N, Vn).
updatearray([(D,array(X,L),_)|T], X, I, N, [(D,array(X,L),_)|T])    :- throw(errOutofbounds(X,N,L,I)).
updatearray([A|T], X, I, N, [A|S])                                  :- updatearray(T, X, I, N, S).

% Update a variable (x = 2)
updatevar([], X, N, [])                             :- throw(errNonexistent(X,N)).
updatevar([(D,X,_)|T], X, N, [(D,X,N)|T])           :- !.
updatevar([A|T], X, N, [A|S])                       :- updatevar(T, X, N, S).

% Delete a block of variables (var x; {var x;})
delblock(D, [(D,_,_)|M], MN)                        :- !, delblock(D, M, MN).
delblock(_, M, M).

:- op(800, xfy, =>).

% Open and end block ({...})
([block(CS)|C], R, [(D,X,N)|M])                     => (CN, R, [(DN,$non,$non),(D,X,N)|M]) :-
                                                       DN is D + 1, append(CS, [endblock|C], CN).
([endblock|C], R, [(D,_,_)|M])                      => (C,R,MN)                     :- delblock(D, M, MN).

% Variable declaration (var x,y[z]...)
([var([])|C], R, M)                                 => (C,              R,  M).
([var([variable(X)|Vars])|C], R, M1)                => ([var(Vars)|C],  R,  M2)     :- newvar(M1, X, $non, M2).
([var([variable(X)=constant|Vars])|C], [N|R], M1)   => ([var(Vars)|C],  R,  M2)     :- newvar(M1, X, N, M2).
([var([variable(X)=A|Vars])|C], R, M)               => ([A,var([variable(X)=constant|Vars])|C], R,  M).
([var([array(X,[A])|Vars])|C], R, M)                => ([A, var([array(X)|Vars])|C],            R,  M).
([var([array(X)|Vars])|C], [N|R], M1)               => ([var(Vars)|C],  R,  M2)     :- newarray(M1, X, N, M2).

% Arrays, constants, cells, variables...
([array(X,[A])|C],  R, M)                           => ([A,array(X)|C], R,      M).
([constant(N)|C],   R, M)                           => (C,              [N|R],  M).
([cell(X)|C],       R, M)                           => (C,              [N|R],  M)  :- lookup(M, X, N), !.
([variable(X) | C], R, M)                           => (C,              [X|R],  M).

% Arithmetic operators
([A|C], R, M)               => ([A1, A2, Op | C], R, M) :- A =.. [Op, A1, A2], memberchk(Op, [+,-,*,//,mod]).
([Op|C], [N2,N1|R], M)      => (C, [N|R], M)            :- memberchk(Op, [+,-,*,mod]), A =.. [Op, N1, N2], N is A.
([//|C], [N2,N1|R], M)      => (C, [N|R], M)            :- N2 is 0 -> throw(errDivisionbyzero(N1,N2)) ; N is N1 // N2.

% Boolean and arithmetic relations
([B|C], R, M)               => ([A1,A2,Op|C], R, M)     :- B =.. [Op, A1, A2], memberchk(Op,    [<,>,>=,=<,=\=,=]).
([Op|C], [N2,N1|R], M)      => (C, [B|R], M)            :- memberchk(Op,                        [<,>,>=,=<,=\=,=]),
                                                           (call(Op, N1, N2) -> B = 1; B = 0).

% minus: -5, -x, -x[3], etc
([neg(A)|C], R, M)          => ([A,neg|C], R, M).
([neg|C], [N|R], M)         => (C, [Nn|R], M)           :- Nn is -N.

% not: !1, !(1<2), etc
([not(B)|C], R, M)          => ([B,not|C],  R,      M).                         % !_
([not|C], [0|R], M)         => (C,          [1|R],  M) :- !.                    % !false
([not|C], [_|R], M)         => (C,          [0|R],  M).                         % !true

% Lazy "&&" and "||": x && y, 1 < x || 1 < y, etc
([and(B1,B2)|C], R, M)      => ([B1,and(B2)|C], R,          M).                 % _ && _
([and(_)|C], [0|R], M)      => (C,              [0|R],      M) :- !.            % false && _
([and(B2)|C], [_|R], M)     => ([B2|C],         R,          M).                 % true && _

([or(B1,B2)|C], R, M)       => ([B1,or(B2)|C],  R,      M).                     % _ || _
([or(B2)|C], [0|R], M)      => ([B2|C],         R,      M) :- !.                % false || _

([or(_)|C], [_|R], M)       => (C,              [1|R],  M).                     % true || _
([or|C], [0|R], M)          => (C,              [0|R],  M).                     % false || false

% assign, if, while, abort...
([B|C], R, M)                           => ([N, X, := | C], R, M)           :- B =.. [:=, X, N].
([:=|C], [X,N|R], M1)                   => (C, R, M2)                       :- updatevar(M1,X,N,M2).
([array(X), :=|C], [I,N|R], M1)         => (C, R, M2)                       :- updatearray(M1,X,I,N,M2).
([array(X) | C], [I|R], M)              => (C, [Nn|R], M)                   :- lookuparray(M,X,I,Nn).

([if(B,C1)|C], R, M)                    => ([B,if|C], [C1|R], M).
([if|C], [0,_C1|R], M)                  => (C, R, M) :- !.
([if|C], [_,C1|R], M)                   => ([C1|C], R, M).

([if(B,C1,C2)|C], R, M)                 => ([B,if_else|C], [C1,C2|R], M).
([if_else|C], [0, _C1, C2 | R], M)      => ([C2|C], R, M) :- !.
([if_else|C], [_, C1, _C2 | R], M)      => ([C1|C], R, M).

([while(B,C1)|C], R, M)                 => ([B, while | C], [B, C1 | R], M).
([while|C], [0, _B, _C1 | R], M)        => (C, R, M) :- !.
([while|C], [_, B, C1 | R], M)          => ([C1,while(B,C1)|C], R, M).

% Just terminate
([abort|_], _, _)                       => ([],[],[]).

% Read, write
([read(variable(X))|C], R, M)           => ([:=|C], [X,N|R], M)     :- read(N).
([write(X)|C], R, M)                    => ([X,write|C], R, M).
([write|C], [X|R], M)                   => (C, R, M)                :- write(X), nl.

:- op(800, xfy, *=>).

% Run the program
InitState *=> FinState :-
    InitState => NextState, !,
    NextState *=> FinState.
FinState *=> FinState.

% Debugger
debugger(InitState) :-
    InitState => NextState, !,
    write(InitState), nl,
    debugger(NextState).
debugger(FinState) :- write(FinState), nl.

% Run lexer and then parse it's output
parse(CharCodeList, Absynt) :-
   phrase(lexer(1,TokList), CharCodeList),
   phrase(program(Absynt), TokList).

% Run from a file, or from the argument (run_file('prog.imp'), run("{write 1;}").)
run_file(File)  :- read_file_to_codes(File, Prog, []), run(Prog).
run(Prog)       :- catch(dorun(Prog), X, errormsg(X)).
dorun(Prog)     :- parse(Prog, P) -> ((P,[],[(0,$non,$non)]) *=> _); throw(errSyntax).

% Run through a debugger.
drun_file(File) :- read_file_to_codes(File, Prog, []), drun(Prog).
drun(Prog)      :- catch(ddorun(Prog), X, errormsg(X)).

ddorun(Prog)    :- parse(Prog, P) -> debugger((P, [], [(0,$non,$non)])); throw(errSyntax).

% Error messages
errormsg(errUnknowntoken(L,X))      :- !, writef("Illegal char \"%n\" on line %d.",[X,L]).

% Only selected errors are reported in detail
errormsg(errParsercf(L,I))          :- !, writef("Incorrect syntax of \"%d\" on line %d.",[I,L]).
errormsg(errSyntax)                 :- !, writef("Incorrect syntax").

% Runtime errors
errormsg(errOutofbounds(X,L,I))     :- !, Ln is L+1,
        writef("Array element \"%d[%d]\" is out of bounds. Array has only %d elements.",[X,I,Ln]).
errormsg(errOutofbounds(X,N,L,I))   :- !, Ln is L+1,
        writef("Array out of bounds: \"%d[%d] = %d\". Array has only %d elements.",[X,I,N,Ln]).
errormsg(errUninited(X))            :- !, writef("Variable \"%d\" is uninitialized",[X]).
errormsg(errUninited(X,N))          :- !, writef("Array element \"%d[%d]\" is uninitialized",[X,N]).
errormsg(errNonexistent(X))         :- !, writef("Attempting to read from a nonexistent variable \"%d\".",[X]).
errormsg(errNonexistent(X,N))       :- !, writef("Attempting to write \"%d\" to a nonexistent variable \"%d\".",[N,X]).
errormsg(errRedeclr(X,D))           :- !, writef("\"%d\" has already been declared in scope level %d.",[X,D]).
errormsg(errArraytosmall(X,N))      :- !, writef("Array size to small: \"var %d[%d]\".",[X,N]).
errormsg(errDivisionbyzero(N1,N2))  :- !, writef("Division by zero: \"%d/%d\".",[N1,N2]).
errormsg(X)                         :- !, writef("Error: \"%d\"", [X]).

Zadanie 6

Z użyciem list.

{-
    Caution: An empty element is denoted as Wire []
    Usage:
    netwalk configuration

    ghci> netwalk [[Source [Right,Down], Wire [Left, Down]],[PC Up, PC Up]]
    [[0,0],[0,0]] 
-}
import Prelude hiding (Right, Left)    -- to avoid confusion
import List                            -- unfoldr
import Maybe                           -- fromMaybe

type Configuration = [[Field]]
type Conflist = [Field]                -- flattened Configuration
type YXPair = (Int,Int)
data Field = Wire [Direction] | Source [Direction] | PC Direction
    deriving (Read, Show, Eq)
data Direction = Up | Right | Down | Left
    deriving (Read, Show, Eq, Ord)     -- Ord is for sort

type Solution = [[Turn]]               -- sometimes -1 is the same as 1
type Turn = Int -- -1 .. 2

-- Interactive
main = do
    configuration <- readLn :: IO Configuration
    print $ netwalk configuration

-- Main function
netwalk :: Configuration -> [Solution]
netwalk configuration =
    let list = foldl (++) [] configuration                             -- flatten the input list
        maxyx = (length configuration, length $ head configuration)    -- maxyx = (maxy,maxx), max_ >= 1
        in
        concatMap (\xs -> if checkConnectivity maxyx xs then [reformat maxyx list xs] else []) (solve maxyx [] list)

-- Try to rotate a piece in four different ways
solve :: YXPair -> Conflist -> Conflist -> [Conflist]
solve _ result [] = [result]
solve maxyx result (x:xs) =
    (if (rotate 0 x) /= Nothing then try maxyx (result ++ fromMaybe [] (rotate 0 x)) xs else []) ++
    (if (rotate 1 x) /= Nothing then try maxyx (result ++ fromMaybe [] (rotate 1 x)) xs else []) ++
    (if (rotate 2 x) /= Nothing then try maxyx (result ++ fromMaybe [] (rotate 2 x)) xs else []) ++
    (if (rotate 3 x) /= Nothing then try maxyx (result ++ fromMaybe [] (rotate 3 x)) xs else [])

-- [] means failure ([] is not a valid netwalk map, therefore we can use it in this way)
try :: YXPair -> Conflist -> Conflist -> [Conflist]
try maxyx result xs = if isProperlyConnected maxyx ((length result) - 1) result then solve maxyx result xs else []

-- Check if on map
inBounds :: YXPair -> YXPair -> Bool
inBounds (maxy, maxx) (y, x)    | y >= 0 && x >= 0 && y < maxy && x < maxx = True
                                | otherwise                                = False

-- Check if Field contains Direction
hasDirNoBounds :: Direction -> Field -> Bool
hasDirNoBounds dir (Wire list) = elem dir list
hasDirNoBounds dir (Source list) = elem dir list
hasDirNoBounds dir (PC list) = dir == list

-- If Field exists, check if it contains Direction
hasDir :: YXPair -> Int -> Direction -> Conflist -> Bool
hasDir maxyx n dir list = if inBounds maxyx (ntoyx maxyx n) then
                              if hasDirNoBounds dir (getN n list) then True else False
                          else False

-- Checks if Field has a valid connection to it's left and upper neighbour (if it exists)
isProperlyConnected :: YXPair -> Int -> Conflist -> Bool
isProperlyConnected maxyx@(maxy,maxx) n list =
    let (y,x) = (ntoyx maxyx n) in
        if y == 0           && (hasDir maxyx n Up list)    then False
        else if y == maxy-1 && (hasDir maxyx n Down list)  then False
        else if x == 0      && (hasDir maxyx n Left list)  then False
        else if x == maxx-1 && (hasDir maxyx n Right list) then False
        else if (hasDir maxyx n Up list)   && y-1 >= 0 && not (hasDir maxyx (yxton maxyx (y-1,x)) Down list)  then False
        else if (hasDir maxyx n Left list) && x-1 >= 0 && not (hasDir maxyx (yxton maxyx (y,x-1)) Right list) then False
        else if not (hasDir maxyx n Up list)   && y-1 >= 0 && (hasDir maxyx (yxton maxyx (y-1,x)) Down list)  then False
        else if not (hasDir maxyx n Left list) && x-1 >= 0 && (hasDir maxyx (yxton maxyx (y,x-1)) Right list) then False
        else True

-- Rotate clockwise
rotateDirection :: Direction -> Direction
rotateDirection Up = Right
rotateDirection Right = Down
rotateDirection Down = Left
rotateDirection Left = Up

-- Rotate a list clockwise
rotateList :: [Direction] -> [Direction]
rotateList [] = []
rotateList (x:xs) = rotateDirection x : rotateList xs

-- Rotate a Field clockwise
rotateElement :: Field -> Field
rotateElement (Wire list)   = Wire (rotateList list)
rotateElement (Source list) = Source (rotateList list)
rotateElement (PC dir)      = PC (rotateDirection dir)

-- Rotate a Field clockwise, n times. rotate 0 = rotate 2 for [Left,Right] etc.
rotate :: Int -> Field -> Maybe [Field]
rotate 0 field                                                    = Just [field]
rotate _ field@(Wire l)     | sort l == sort [Left,Right,Up,Down] = Nothing
rotate _ field@(Source l)   | sort l == sort [Left,Right,Up,Down] = Nothing
rotate _ field@(Wire [])                                          = Nothing
rotate 1 field                                                    = Just $ [rotate' 1 field]
rotate _ field@(Wire l)     | sort l == sort [Left,Right]         = Nothing
                            | sort l == sort [Up,Down]            = Nothing
rotate _ field@(Source l)   | sort l == sort [Left,Right]         = Nothing
                            | sort l == sort [Up,Down]            = Nothing
rotate 2 field                                                    = Just $ [rotate' 2 field]
rotate 3 field                                                    = Just $ [rotate' 3 field]

rotate' :: Int -> Field -> Field
rotate' 0 field = field
rotate' n field = rotate' (n-1) (rotateElement field)

{- ******************************** Check connectivity ********************************* -}

type EnumeratedField = (YXPair,Field)
type EnumeratedConflist = [EnumeratedField] -- Conflist with coordinates

checkConnectivity :: YXPair -> Conflist -> Bool
checkConnectivity maxyx list = checkConn listNoEmpty listEmpty [source]
    where
        enumeratedList = enumerateConflist maxyx 0 list
        source@(sourceYX,_) = getSource enumeratedList
        listNoSource = delYX sourceYX enumeratedList 
        (listEmpty,listNoEmpty) = checkConnAddEmpty listNoSource [] []

-- Add all empty elements to one graph
checkConnAddEmpty :: EnumeratedConflist -> EnumeratedConflist -> EnumeratedConflist -> (EnumeratedConflist,EnumeratedConflist)
checkConnAddEmpty [] list1 list2                     = (list1,list2)
checkConnAddEmpty (el@(yx,Wire []):list) list1 list2 = checkConnAddEmpty list (el:list1) (list2)
checkConnAddEmpty (el:list) list1 list2              = checkConnAddEmpty list (list1) (el:list2)

-- Check to see if the graph is connective
checkConn :: EnumeratedConflist -> EnumeratedConflist -> EnumeratedConflist -> Bool
checkConn input result [] = containsOnlyWires input
    where
        containsOnlyWires [] = True
        containsOnlyWires ((yx,Wire _):list) = containsOnlyWires list
        containsOnlyWires _ = False
checkConn input result (el@((y,x),val):buffer) = checkConn input' (el:result) (buffer ++ bufs)
    where
        getUp    = if hasDirNoBounds Up val    then getYX (y-1,x) input else Nothing
        getDown  = if hasDirNoBounds Down val  then getYX (y+1,x) input else Nothing
        getLeft  = if hasDirNoBounds Left val  then getYX (y,x-1) input else Nothing
        getRight = if hasDirNoBounds Right val then getYX (y,x+1) input else Nothing
        bufs = concatMap (\x -> if x == Nothing then [] else [fromJust x]) [getUp,getDown,getLeft,getRight]
        input' = input \\ bufs
        
{- ***************************** Formating, list operations ********************************* -}

-- Reformat a list of Fields into Solution
-- Sometimes -1 is the same as 1 (Wire [Left, Right])
reformat :: YXPair -> Conflist -> Conflist -> Solution
reformat (maxy,maxx) xs ys = unfoldr (\x -> if x == [] then Nothing else Just $ splitAt maxx x) $ reformatFlat xs ys
    -- unfoldr deflattenes a list

-- Convert from Conflist to [Turn]. For input (PC Right) and (PC Down) this function gives 1
reformatFlat :: Conflist -> Conflist -> [Turn]
reformatFlat [] [] = []
reformatFlat (x:xs) (y:ys) = 
    (    if x == rotate' 0 y then 0
    else if x == rotate' 1 y then 1
    else if x == rotate' 2 y then 2
    else -1)
    : reformatFlat xs ys

-- Let all elements of a 1D list hold the coordinates of an element
enumerateConflist :: YXPair -> Int -> Conflist -> EnumeratedConflist
enumerateConflist maxyx n [] = []
enumerateConflist maxyx n (x:xs) = (ntoyx maxyx n,x) : (enumerateConflist maxyx (n+1) xs)

-- Remove above-mentioned coordinates
removeCoordinates :: EnumeratedConflist -> Conflist
removeCoordinates [] = []
removeCoordinates (x:xs) = (snd x) : (removeCoordinates xs)

getSource :: EnumeratedConflist -> EnumeratedField
getSource (el@(_,Source _):xs) = el
getSource (el:xs) = getSource xs

delYX :: YXPair -> EnumeratedConflist -> EnumeratedConflist
delYX yx (el@(yx',val):xs) | yx == yx' = xs
                           | otherwise = el : delYX yx xs

getYX :: YXPair -> EnumeratedConflist -> Maybe EnumeratedField
getYX yx []                            = Nothing
getYX yx (el@(yx',val):xs) | yx == yx' = Just el
                           | otherwise = getYX yx xs

-- Convert between (y,x) to n and back
yxton :: YXPair -> YXPair -> Int
yxton (maxy, maxx) (y,x) = y*maxx + x

ntoyx :: YXPair -> Int -> YXPair
ntoyx (maxy, maxx) n = (n `div` maxx, n `mod` maxx)

-- Get n'th Field
getN :: Int -> Conflist -> Field
getN n xs = xs !! n

Zadanie 7

Z użyciem list.

{-
    Interactive usage in ghci:
    arrows [[Left]]
-}
import Prelude hiding (Right, Left) -- to avoid confusion
import List
import Maybe

{- ****************************************************************************************** -}
type Board = [[Field]]
data Field =    Up | Right | Down | Left | UpN Int | RightN Int | DownN Int | LeftN Int |
                UpL [Int] | RightL [Int] | DownL [Int] | LeftL [Int]        -- auxilary constructors
                    deriving (Read, Show, Eq)

type Conflist = [Field]                            -- flattened Configuration
type EnumeratedField = (YXPair,Field)
type EnumeratedConflist = [EnumeratedField]        -- Conflist with coordinates
type YXPair = (Int,Int)

type Solution = [[Numbr]]
type Numbr = Int -- 1 .. n-1

-- Interactive
main = do
    board <- readLn :: IO Board
    print $ arrows board

-- Main predicate
arrows :: Board -> Board
arrows board =
    let list       = foldl (++) [] board                            -- flatten the input list
        maxyx      = (length board, length $ head board)            -- maxyx = (maxy,maxx), max_ >= 1
        enumList   = enumerateConflist maxyx 0 list                 -- reformat the input list
        bordered   = map (arrowBorder maxyx) enumList               -- simple optimization, adds _N 0
        rnged      = map (arrowRng maxyx bordered) bordered         -- adds [min..max] fields. After this
                                                                    -- there are no more Up, Right etc
                                                                    -- (only UpL, RightL...)
        ltoned     = map (arrowLtoN) rnged                          -- convert UpL [x] to UpN x
        optimized  = arrowOptimize maxyx ltoned                     -- optimize input as much as possible
                                                                    -- (greatly decreases runtime)
        in
            head $ (map (reformat maxyx) $ map removeCoordinates $ concatMap (\xs -> if checkMap maxyx xs xs then [xs] else []) $ solve maxyx optimized []) ++ [[]]
-- append an empty list, so that head will always succeed
-- NOTE: Displaying all results can be achieved by removing "head" from the above function.
--       The type of the function will change.

{- **************************************** FORMATTING ***************************************** -}

-- Convert 1D list to 2D
reformat :: YXPair -> Conflist -> Board
reformat (maxy,maxx) list = unfoldr (\x -> if x == [] then Nothing else Just $ splitAt maxx x) list

-- Let all elements of a 1D list hold the coordinates of an element
enumerateConflist :: YXPair -> Int -> Conflist -> EnumeratedConflist
enumerateConflist maxyx n [] = []
enumerateConflist maxyx n (x:xs) = (ntoyx maxyx n,x) : (enumerateConflist maxyx (n+1) xs)

-- Remove above-mentioned coordinates
removeCoordinates :: EnumeratedConflist -> Conflist
removeCoordinates [] = []
removeCoordinates (x:xs) = (snd x) : (removeCoordinates xs)

{- ************************************** SOLVING THE MAP ************************************** -}

-- Protection from malicious input, like [[LeftN 10]].
-- The function checkMap checks a solved map to see if it is correct, using no optimization at all.
-- Caution: If we assume that input may not be malicious then these two function are useless!
countDN :: [Field] -> Int
countDN list = length $ nub list' where
                list' = [fromJust (arrowGetValN e) | e <- list]

checkMap :: YXPair -> EnumeratedConflist -> EnumeratedConflist -> Bool
checkMap (maxy,maxx) [] list = True
checkMap (maxy,maxx) (((y,x),val):xs) list = 
    if arrowIsUpN    val then
        if (fromJust $ arrowGetValN val) == countDN ([getYX (y',x) list | y' <- [0..y-1]]) then
            checkMap (maxy,maxx) xs list
        else False
    else if arrowIsDownN  val then
        if (fromJust $ arrowGetValN val) == countDN ([getYX (y',x) list | y' <- [y+1..maxy-1]]) then
            checkMap (maxy,maxx) xs list
        else False
    else if arrowIsLeftN  val then
        if (fromJust $ arrowGetValN val) == countDN ([getYX (y,x') list | x' <- [0..x-1]]) then
            checkMap (maxy,maxx) xs list
        else False
    else -- if arrowIsRightN val then
        if (fromJust $ arrowGetValN val) == countDN ([getYX (y,x') list | x' <- [x+1..maxx-1]]) then
            checkMap (maxy,maxx) xs list
        else False

-- Return all valid results. Solve uses an accumulator.
-- The function solve' is ugly (it's arguments are), but it's quite fast. The number of calculations
-- required is minimal (without this optimization some calculations would need to be performed multiple times).
solve :: YXPair -> EnumeratedConflist -> EnumeratedConflist -> [EnumeratedConflist]
solve maxyx [] acc = [reverse acc]
solve maxyx@(my,mx) ((el@(yx@(y,x),val)):list) acc
    | arrowSure val    = solve maxyx list (el:acc)
    | otherwise        =  -- val == _L [a,b,c...]   -- at this point val cannot be Up, Down etc
            solve' maxyx (head p) (last p) (last p) el list acc (vera,verb,hora,horb,vera',verb',hora',horb', (eva,nva), (evb,nvb), (eha,nha), (ehb,nhb))
            where
                p = fromJust $ arrowGetValL val

{-     (vera)
(hora)   el   (horb)
       (verb)   for element el get the column and row it's in. -}
                -- get column, row...
                vera = map snd $ reverse $ getColumn x acc
                verb = map snd $ getColumn x list
                hora = map snd $ reverse $ take x acc
                horb = map snd $ take (mx-x-1) list

                -- get value, if it exists.
                -- For Up 2 value = Just 2
                -- For UpL [1,2] value = Nothing
                vera' = map arrowGetValN $ vera
                verb' = map arrowGetValN $ verb
                hora' = map arrowGetValN $ hora
                horb' = map arrowGetValN $ horb

                -- ugly but efficient hack: divide Nothings and Justs
                (eva,nva) = partition (== Nothing) $ vera'
                (evb,nvb) = partition (== Nothing) $ verb'
                (eha,nha) = partition (== Nothing) $ hora'
                (ehb,nhb) = partition (== Nothing) $ horb'

-- solve' attempts to assign all possible values to an arrow. If the value is potentially correct it runs solve.
-- Assign values from maximum to minimum (if UpL [1,2,3,4] then start from 4). If a map is large, there is
-- a chance numbers will be large as well. If a map is small it will be solved quickly anyway.
solve' maxyx@(my,mx) start cur end (yx@(y,x),val) list acc tuple@(vera,verb,hora,horb,vera',verb',hora',horb', (eva,nva), (evb,nvb), (eha,nha), (ehb,nhb)) =
    if cur < start then [] else
    ((if    (checkMainElement (snd nel) (eva,nva) (evb,nvb) (eha,nha) (ehb,nhb)) &&
            (checkElementsHorizontal horizlist horizlist' (ehl,nhl)) &&
            (checkElementsVertical vertlist vertlist' (evl,nvl))
    then cont
    else []) ++ trydifferent)
    where
        trydifferent = solve' maxyx start (cur-1) end (yx,val) list acc tuple
        nel@(_,nval) = (yx,arrowAssign val cur)
        cont = solve maxyx list (nel:acc)

        (ee,ne) = partition (== Nothing) [arrowGetValN $ snd nel]
        (evl,nvl) = (eva++ee++evb,nva++ne++nvb)
        (ehl,nhl) = (eha++ee++ehb,nha++ne++nhb)

        vertlist = vera ++ (snd nel) : verb
        horizlist = hora ++ (snd nel) : horb
        vertlist' = vera' ++ (arrowGetValN $ snd nel) : verb'
        horizlist' = hora' ++ (arrowGetValN $ snd nel) : horb'
        

-- count Different Numbers Optimistic, i.e treat unknown values of arrows as potentially being the same
-- as other values in the set.
countDNO :: [Field] -> Int
countDNO list = if len == 0 then 1 else len    
                where
                    len = length $ nub $ filter (/= Nothing) list'
                    list' = [(arrowGetValN e) | e <- list]

-- The same as above, but Pesimistic -- treats unknown values as being different from others.
countDNP :: [Field] -> Int
countDNP list = (length $ nub $ filter (/= Nothing) list') + (length $ filter (== Nothing) list')
                where
                    list' = [(arrowGetValN e) | e <- list]

-- Calculate the range of possible values
countDNOP :: [Field] -> [Int]
countDNOP [] = [0]
countDNOP list = [(countDNO list)..(countDNP list)]

-- Same as above, but most of the work is done by solve'
countDNOP' :: ([Maybe Int],[Maybe Int]) -> [Int]
countDNOP' (eq,neq) = [flistl..(flistl + flistl')]
                    where
                        flistl = length $ nub neq
                        flistl' = length eq

checkElementsHorizontal [] _ _ = True
checkElementsHorizontal (el:elems) (_:wval) ([],_:neqs) =
    if (checkElementRight el ([],neqs)) then (checkElementsHorizontal elems wval ([],neqs))
    else False

checkElementsHorizontal (el:elems) (_:wval) (_:eqs,[]) =
    if (checkElementRight el (eqs,[])) then (checkElementsHorizontal elems wval (eqs,[]))
    else False

checkElementsHorizontal (el:elems) (wv:wval) (eq:eqs,neq:neqs) =
    if wv == Nothing then
        if (checkElementRight el (eqs,neq:neqs)) then (checkElementsHorizontal elems wval (eqs,neq:neqs))
        else False
    else
        if (checkElementRight el (eq:eqs,neqs)) then (checkElementsHorizontal elems wval (eq:eqs,neqs))
        else False

checkElementRight val list =
    if arrowIsRightN val then
        if elem (fromJust $ arrowGetValN val) (countDNOP' list) then True
        else False
    else True

checkElementsVertical [] _ _ = True
checkElementsVertical (el:elems) (_:wval) ([],_:neqs) =
    if (checkElementDown el ([],neqs)) then (checkElementsVertical elems wval ([],neqs))
    else False

checkElementsVertical (el:elems) (_:wval) (_:eqs,[]) =
    if (checkElementDown el (eqs,[])) then (checkElementsVertical elems wval (eqs,[]))
    else False

checkElementsVertical (el:elems) (wv:wval) (eq:eqs,neq:neqs) =
    if wv == Nothing then
        if (checkElementDown el (eqs,neq:neqs)) then (checkElementsVertical elems wval (eqs,neq:neqs))
        else False
    else
        if (checkElementDown el (eq:eqs,neqs)) then (checkElementsVertical elems wval (eq:eqs,neqs))
        else False

checkElementDown val list =
    if arrowIsDownN val then
        if elem (fromJust $ arrowGetValN val) (countDNOP' list) then True
        else False
    else True

checkMainElement val vera verb hora horb =
    if arrowIsRightN val then
        if elem (fromJust $ arrowGetValN val) (countDNOP' horb) then True
        else False
    else if arrowIsDownN val then
        if elem (fromJust $ arrowGetValN val) (countDNOP' verb) then True
        else False
    else if arrowIsLeftN val then 
        if [fromJust $ arrowGetValN val] == (countDNOP' hora) then True
        else False
    else if arrowIsUpN val then 
        if [fromJust $ arrowGetValN val] == (countDNOP' vera) then True
        else False
    else False

{- ********************************** INITIAL OPTIMALIZATION *********************************** -}
-- Run only once per problem, no need to be fast
-- The only function that assigns values of 0
arrowBorder :: YXPair -> EnumeratedField -> EnumeratedField
arrowBorder (my,mx) el@(yx@(y,x),val) =
        if arrowSure val then el     -- Already sure
        else if arrowIsUp val       && y == 0 then       (yx,UpN 0)
        else if arrowIsDown val     && y == my-1 then    (yx,DownN 0)
        else if arrowIsLeft val     && x == 0 then       (yx,LeftN 0)
        else if arrowIsRight val    && x == mx-1 then    (yx,RightN 0)
        else el

-- Convert UpL [1] to UpN 1 etc.
arrowLtoN :: EnumeratedField -> EnumeratedField
arrowLtoN el@(yx,val) =
    let
        list = fromMaybe [] (arrowGetValL val)
        singleton [x] = x
    in
        if (length list == 1) then
            if arrowIsUpL val then          (yx,UpN $ singleton list)
            else if arrowIsDownL val then   (yx,DownN $ singleton list)
            else if arrowIsLeftL val then   (yx,LeftN $ singleton list)
            else                            (yx,RightN $ singleton list)
        else
            el
                            
-- Assign each Up, Down etc. a maximum range UpL [min..max] etc.
arrowRng :: YXPair -> EnumeratedConflist -> EnumeratedField -> EnumeratedField
arrowRng maxyx@(my,mx) list el@(yx@(y,x),val)
    | arrowIsUp    val    = arrowLtoN (yx,UpL    $ countDNOP [getYX' maxyx (y',x) list | y' <- [0..y-1]])
    | arrowIsDown  val    = arrowLtoN (yx,DownL  $ countDNOP [getYX' maxyx (y',x) list | y' <- [y+1..my-1]])
    | arrowIsLeft  val    = arrowLtoN (yx,LeftL  $ countDNOP [getYX' maxyx (y,x') list | x' <- [0..x-1]])
    | arrowIsRight val    = arrowLtoN (yx,RightL $ countDNOP [getYX' maxyx (y,x') list | x' <- [x+1..mx-1]])
    | otherwise           = el

-- Various optimizations. For example [..., LeftN 3, Left, ...] gives [..., LeftN 3, LeftL [3,4], ...]
arrowOptimize :: YXPair -> EnumeratedConflist -> EnumeratedConflist
arrowOptimize maxyx list = if down == list then down else arrowOptimize maxyx down
    where
        left    = arrowLeftOptimize maxyx list []
        right   = arrowRightOptimize maxyx (reverse left) []
        up      = arrowUpOptimize maxyx right []
        down    = arrowDownOptimize maxyx (reverse up) []

arrowLeftOptimize :: YXPair -> EnumeratedConflist -> EnumeratedConflist -> EnumeratedConflist
arrowLeftOptimize maxyx [] acc = reverse acc
arrowLeftOptimize maxyx (el@(yx@(y,x),val):list) acc =
    if arrowIsLeftL val || arrowIsLeft val then
        arrowLeftOptimize maxyx list ((arrowLtoN(yx,LeftL $ countDNOP [getYX (y,x') acc | x' <- [0..x-1]])):acc)
    else arrowLeftOptimize maxyx list (el:acc)
    
arrowRightOptimize :: YXPair -> EnumeratedConflist -> EnumeratedConflist -> EnumeratedConflist
arrowRightOptimize maxyx [] acc = acc
arrowRightOptimize maxyx@(my,mx) (el@(yx@(y,x),val):list) acc =
    if arrowIsRightL val || arrowIsRight val then
        arrowRightOptimize maxyx list ((arrowLtoN(yx,RightL $ countDNOP [getYX (y,x') acc | x' <- [x+1..mx-1]])):acc)
    else arrowRightOptimize maxyx list (el:acc)
    
arrowUpOptimize :: YXPair -> EnumeratedConflist -> EnumeratedConflist -> EnumeratedConflist
arrowUpOptimize maxyx [] acc = reverse acc
arrowUpOptimize maxyx (el@(yx@(y,x),val):list) acc =
    if arrowIsUpL val || arrowIsUp val then
        arrowUpOptimize maxyx list ((arrowLtoN(yx,UpL $ countDNOP [getYX (y',x) acc | y' <- [0..y-1]])):acc)
    else arrowUpOptimize maxyx list (el:acc)
    
arrowDownOptimize :: YXPair -> EnumeratedConflist -> EnumeratedConflist -> EnumeratedConflist
arrowDownOptimize maxyx [] acc = acc
arrowDownOptimize maxyx@(my,mx) (el@(yx@(y,x),val):list) acc =
    if arrowIsDownL val || arrowIsDown val then
        arrowDownOptimize maxyx list ((arrowLtoN(yx,DownL $ countDNOP [getYX (y',x) acc | y' <- [y+1..my-1]])):acc)
    else arrowDownOptimize maxyx list (el:acc)

{- ************************************** DATA OPERATIONS ************************************** -}
arrowAssign    :: Field -> Int -> Field
arrowAssign (UpL _) n       = UpN n
arrowAssign (RightL _) n    = RightN n
arrowAssign (DownL _) n     = DownN n
arrowAssign (LeftL _) n     = LeftN n

arrowIsUp Up                = True
arrowIsUp _                 = False
arrowIsUpL (UpL _)          = True
arrowIsUpL _                = False
arrowIsUpN (UpN _)          = True
arrowIsUpN _                = False
arrowIsDown Down            = True
arrowIsDown _               = False
arrowIsDownL (DownL _)      = True
arrowIsDownL _              = False
arrowIsDownN (DownN _)      = True
arrowIsDownN _              = False
arrowIsRight Right          = True
arrowIsRight _              = False
arrowIsRightL (RightL _)    = True
arrowIsRightL _             = False
arrowIsRightN (RightN _)    = True
arrowIsRightN _             = False
arrowIsLeft Left            = True
arrowIsLeft _               = False
arrowIsLeftL (LeftL _)      = True
arrowIsLeftL _              = False
arrowIsLeftN (LeftN _)      = True
arrowIsLeftN _              = False

arrowSure :: Field -> Bool
arrowSure (UpN _)           = True
arrowSure (RightN _)        = True
arrowSure (DownN _)         = True
arrowSure (LeftN _)         = True
arrowSure _                 = False

arrowGetValN :: Field -> Maybe Int
arrowGetValN (UpN n)        = Just n
arrowGetValN (RightN n)     = Just n
arrowGetValN (DownN n)      = Just n
arrowGetValN (LeftN n)      = Just n
arrowGetValN _              = Nothing

arrowGetValL :: Field -> Maybe [Int]
arrowGetValL (UpL n)        = Just n
arrowGetValL (RightL n)     = Just n
arrowGetValL (DownL n)      = Just n
arrowGetValL (LeftL n)      = Just n
arrowGetValL _              = Nothing

{- **************************************** COORDINATES **************************************** -}
-- Convert between (y,x) to n and back
yxton        :: YXPair -> YXPair -> Int
yxton (maxy, maxx) (y,x) = y*maxx + x

ntoyx        :: YXPair -> Int -> YXPair
ntoyx (maxy, maxx) n = (n `div` maxx, n `mod` maxx)

-- Get n'th Field
getN        :: Int -> Conflist -> Field
getN n xs = xs !! n

-- Get (y,x)'th field. Functions using getYX are *SURE* this element exists
getYX :: YXPair -> EnumeratedConflist -> Field
getYX yx ((yx',val):list) | yx == yx'   = val
                          | otherwise   = getYX yx list

-- Get all elements with the second coordinate == x
getColumn :: Int -> EnumeratedConflist -> EnumeratedConflist
getColumn x [] = []
getColumn x (el@((_,x'),_):list)    | x == x'    = el:(getColumn x list)
                                    | otherwise    = getColumn x list

-- Optimized getYX used by optimized functions
getYX' :: YXPair -> YXPair -> EnumeratedConflist -> Field
getYX' maxyx yx list = snd $ list !! (yxton maxyx yx)

Zadanie 8


 
programowanie/pracownia.txt · ostatnio zmienione: 2009/10/11 13:49 przez 90.156.109.136
 
Wszystkie treści w tym wiki, którym nie przyporządkowano licencji, podlegają licencji:MIT License
Recent changes RSS feed