Code covered by the BSD License  

Highlights from
Carcassonne

image thumbnail

Carcassonne

by

 

21 Jan 2012 (Updated )

Emulation of the map-building game Carcassonne, including 4 expansion packs

CheckPos(game, tilenum, row, col, orientation)
function ok = CheckPos(game, tilenum, row, col, orientation)

% Checks if a tile was placed according to the rules. This means that all
% sides must match their neighboring sides, and that there must be at least
% 1 neighboring tile present, i.e. no islands.

G = get(game.imgH, 'UserData');
specs = get(game.figH, 'UserData');

ok = false;
%% MOST ELEMENTARY CHECK: IS IT AN EMPTY SPACE?
if any([row, col] > size(G,1)) || G(row, col, 1) ~= 0
    return
end

%% WIDEN THE FIELD A LITTLE
% Place field in a new (temporary) field to avoid boundary issues 
% when checking the adjecent tiles
n = size(G,1);
Gtemp = zeros(n + 2, n + 2, 4);
Gtemp(2:end-1, 2:end-1, :) = G;

%% FIND NEIGHBORS
i = row + 1;
j = col + 1;

% Adjecent tiles (numbers)
adjecNums = [Gtemp(i-1, j, 1);...  N
             Gtemp(i, j+1, 1);...  E
             Gtemp(i+1, j, 1);...  S
             Gtemp(i, j-1, 1)];   %W

% Adjecent tiles (orientations)
adjecOris = [Gtemp(i-1, j, 2);...  N
             Gtemp(i, j+1, 2);...  E
             Gtemp(i+1, j, 2);...  S
             Gtemp(i, j-1, 2)];   %W

%% CHECK FOR NEIGHBORS
if ~any(adjecNums)
    return          % No adjecent tiles
end
         
%% SIDES AFTER ROTATION
HomeSides = rotate(specs(tilenum, 2:3:11), orientation);

%% CHECK IF SIDES MATCH THOSE OF NEIGHBORS
opposite = @(x) mod(x+2, 4) + 4 * (mod(x+2, 4) == 0);
for side = 1:4 % for each side/adjecent tile
  
    if adjecNums(side) == 0
        continue            % empty
    end
    
    % Sides of adjecent tile after rotation
    OtherSides = rotate(specs(adjecNums(side), 2:3:11), adjecOris(side));
    
    % Check sides
    HomeSides(HomeSides == 4) = 2;
    OtherSides(OtherSides == 4) = 2;
    if HomeSides(side) ~= OtherSides(opposite(side))
        return;
    end
end

ok = true;

if ~game.playRivers
    return
end

if ~any(specs(tilenum, 1:12) == 5)
    return  % not a river-tile
end

%% RIVER PHASE
% The official rules tell us that a river is NOT allowed to make U-turns.
% This is in order to shrink the chance of creating an unfinished river.
% However, it is still posible to create an unfinished river using only
% this rule. This is why I add 2 additional rules that will ensure a river
% to be finished, ending in the lake:
% 1. Rivers have to be placed 'river-to-river'.
% 2. A river may NOT flow East-West (opposite to start-direction)
% 
% The user may uncheck the forcing of a completed river. In this case, a
% river-tile is just any other tile, no rules whatsoever!

if ~game.forcecompletedriver
    return
end

ok = false;

% STEP 1: Check if the tile was placed 'river-to-river'
riverside = find(HomeSides == 5);
other = adjecNums(riverside);

if all(other == 0)
    return
end

% STEP 2: Check if the river flows East-West
if riverside(other == 0) == 4
    return                     % river exits to the West
end

% STEP 3: Check for U-turns (north-south and vice versa)
% Entrance-side of 'other' may not equal Exitside of current tile.
riverside = riverside(other ~=0);
other = other(other ~= 0);
otherSides = rotate(specs(other, 2:3:11), adjecOris(riverside));
entranceOther = find(otherSides == 5);
entranceOther = entranceOther(entranceOther ~= opposite(riverside));
if isempty(entranceOther)
    ok = true;
    return
end
exitCurrent = find(HomeSides == 5);
exitCurrent = exitCurrent(exitCurrent ~= riverside);
if entranceOther == exitCurrent
    return
end

% OK!
ok = true;






Contact us