function food = armyantsimulator(contestAntFH,houseAntFH,problem,drawboard)
% MATLAB 2008 Army Ants Contest
%
% RESULT = ARMYANTSIMULATOR(contestAntFH,houseAntFH,problem,drawboard)
%
% contestAntFH - function handle to the contest"ANT" microprogram
% houseAntFH - function handle to the "king of the hill" microprogram
% problem - structure with the fields: main, food, red, black,
% redScent, blackScent, redDeath, blackDeath
% drawboard - logical flag to show the map
% Copyright 2008 The MathWorks, Inc.
if nargin<4
drawboard = true;
end
main = problem.main;
food = problem.food;
red = problem.red;
black = problem.black;
redScent = zeros(size(main));
blackScent = zeros(size(main));
redDeath = zeros(size(main));
blackDeath = zeros(size(main));
if drawboard
viewantmap(main,food,red,black,redScent,blackScent,redDeath,blackDeath,true)
end
for timeStep = 1:1000
mergeList = getMergeList(red, black);
if ~isstruct(mergeList) && isnan(mergeList)
return
end
actions = getActions(mergeList, main, food, red, black, redScent, blackScent, redDeath, blackDeath, contestAntFH, houseAntFH);
[main, food, red, black, redScent, blackScent, redDeath, blackDeath] = ...
updateWorld(actions, main, food, red, black, redScent, blackScent, redDeath, blackDeath, mergeList);
if drawboard
viewantmap(main,food,red,black,redScent,blackScent,redDeath,blackDeath,false,timeStep)
end
end
end
function actions = getActions(mergeList, main, food, red, black, redScent, blackScent, redDeath, blackDeath, contestAntFH, houseAntFH)
actions(numel(mergeList)).r = []; %preallocate
for curAnt = 1 : numel(mergeList)
[AVmain, AVfood, AVred, AVblack, AVredScent, AVblackScent, AVredDeath, AVblackDeath] = ...
getAntView(mergeList(curAnt),main, food, red, black, redScent, blackScent, redDeath, blackDeath);
if mergeList(curAnt).team == 'r'
[actions(curAnt).dRow, actions(curAnt).dCol, actions(curAnt).action, actions(curAnt).mark] ...
= contestAntFH(-AVmain, AVfood, AVred, AVblack, AVredScent, AVblackScent, AVredDeath, AVblackDeath);
else
try
[actions(curAnt).dRow, actions(curAnt).dCol, actions(curAnt).action, actions(curAnt).mark] ...
= houseAntFH(AVmain, AVfood, AVblack, AVred, AVblackScent, AVredScent, AVblackDeath, AVredDeath);
catch %#ok<CTCH>
actions(curAnt).dRow = 0;
actions(curAnt).dCol = 0;
actions(curAnt).action = 0;
actions(curAnt).mark = 0;
end
end
% actions(curAnt) = safeCheckAction(actions(curAnt)); %CHECK TOO SLOW.
% TOO BAD IF YOUR INPUTS BUST CODE
actions(curAnt).team = mergeList(curAnt).team;
actions(curAnt).r = mergeList(curAnt).r;
actions(curAnt).c = mergeList(curAnt).c;
end
end
function [main, food, red, black, redScent, blackScent, redDeath, blackDeath] = ...
updateWorld(actions, main, food, red, black, redScent, blackScent, redDeath, blackDeath, mergeList)
%pharamone phase
for curAnt = 1 : numel(mergeList)
[redScent, blackScent] = pharamoneDrop(actions(curAnt), redScent, blackScent);
end
%move phase
for curAnt = 1 : numel(mergeList)
[red, black, food, actions(curAnt)] = moveAnt(actions(curAnt), food, red, black, main);
end
%attack and death phase
for curAnt = 1 : numel(mergeList)
[red, black, redDeath, blackDeath] = attackAnt(actions(curAnt), red, black, redDeath, blackDeath);
end
%lessen pharamone and deaths head
redDeath = max( redDeath-1, 0);
blackDeath = max(blackDeath-1, 0);
redScent = max( redScent-1, 0);
blackScent = max(blackScent-1, 0);
end
function [redScent, blackScent] = pharamoneDrop(action, redScent, blackScent)
if action.mark < 0
action.mark = 0;
elseif action.mark > 100
action.mark = 100;
end
action.mark = round(action.mark);
if action.team == 'r'
redScent(action.r, action.c) = redScent(action.r, action.c) + action.mark;
end
if action.team == 'b'
blackScent(action.r, action.c) = blackScent(action.r, action.c) + action.mark;
end
end
function [red, black, food, action] = moveAnt(action, food, red, black, main)
flagGoodRow = (action.dRow == -1) |...
(action.dRow == 0) |...
(action.dRow == 1);
flagGoodCol = (action.dCol == -1) |...
(action.dCol == 0) |...
(action.dCol == 1);
if (flagGoodRow && flagGoodCol)
newR = action.r + action.dRow;
newC = action.c + action.dCol;
if ~isnan(main(newR, newC)) %not obstacle
if (action.action == 1) && ...
(food(action.r, action.c) > 0) %carry and there is food
food(action.r, action.c) = food(action.r, action.c) - 1;
food(newR, newC) = food(newR, newC) + 1;
end %move sugar
if action.team == 'r'
red (action.r, action.c) = red (action.r, action.c) - 1;
red (newR,newC) = red (newR,newC) + 1;
end
if action.team == 'b'
black(action.r, action.c) = black(action.r, action.c) - 1;
black(newR,newC) = black(newR,newC) + 1;
end
action.r = newR; %move ant
action.c = newC; %move ant
end
else %not a good move
return
end
end
function [red, black, redDeath, blackDeath] = attackAnt(actions, red, black, redDeath, blackDeath)
if (actions.dRow == 0) && ...
(actions.dCol == 0) && ...
(actions.action == -1) %Not moving, and attacking
if (actions.team == 'r') && (black(actions.r, actions.c) > 0)
black (actions.r, actions.c) = black (actions.r, actions.c) - 1;
blackDeath(actions.r, actions.c) = blackDeath(actions.r, actions.c) + 100;
elseif (actions.team == 'b') && ( red(actions.r, actions.c) > 0)
red (actions.r, actions.c) = red (actions.r, actions.c) - 1;
redDeath(actions.r, actions.c) = redDeath(actions.r, actions.c) + 100;
end
end
end
function [AVmain, AVfood, AVred, AVblack, AVredScent, AVblackScent, AVredDeath, AVblackDeath]...
= getAntView(currA, main, food, red, black, redScent, blackScent, redDeath, blackDeath)
dist = [-2 -1 0 1 2];
dr = currA.r + dist;
dc = currA.c + dist;
AVmain = main (dr, dc);
AVfood = food (dr, dc);
AVred = red (dr, dc);
AVblack = black (dr, dc);
AVredScent = redScent (dr, dc);
AVblackScent = blackScent(dr, dc);
AVredDeath = redDeath (dr, dc);
AVblackDeath = blackDeath(dr, dc);
end
function viewantmap(main,food,red,black,redScent,blackScent,redDeath,blackDeath,reset, timeStep)
% shows the ant maps in a MATLAB figure
f = findobj(0,'Tag','AntMap');
if reset || isempty(f) % create new one
if isempty(f)
f = figure('Tag','AntMap');
else % reuse old AntMap figure
clf
figure(f)
end
cMap = flipud(pink(100));
cMap = cMap(1:75,:);
colormap(cMap)
topScent = max([redScent(:); blackScent(:); 1]);
handle.axisrs = subplot(2,2,1);
handle.imagers = imagesc(redScent);
title('Scent Red')
[handle.blackants{1} handle.redants{1} handle.food(1,:)] = showFeatures(main, food, red, black);
handle.axisbs = subplot(2,2,2);
handle.imagebs = imagesc(blackScent);
title('Scent Black')
[handle.blackants{2} handle.redants{2} handle.food(2,:)] = showFeatures(main, food, red, black);
topDeath = max([redDeath(:); blackDeath(:); 1]);
handle.axisrd = subplot(2,2,3);
handle.imagerd = imagesc(redDeath);
set(handle.axisrd, 'clim',[0 topDeath])
title(['Death Red, with ' num2str(sum(red(:))) ' red ants alive.'])
[handle.blackants{3} handle.redants{3} handle.food(3,:)] = showFeatures(main, food, red, black);
handle.axisbd = subplot(2,2,4);
handle.imagebd = imagesc(blackDeath);
set(handle.axisbd, 'clim',[0 topDeath])
title(['Death Black, with ' num2str(sum(black(:))) ' black ants alive.'])
[handle.blackants{4} handle.redants{4} handle.food(4,:)] = showFeatures(main, food, red, black);
setappdata(f,'AntHandles',handle)
set(f,'MenuBar','none')
drawnow
else % AntMap exists, then just update
handle = getappdata(f,'AntHandles');
[xx,yy] = getSpots(black);
set(cell2mat(handle.blackants),'Xdata',xx,'Ydata',yy+.25)
[xx,yy] = getSpots(red);
set(cell2mat(handle.redants),'Xdata',xx,'Ydata',yy-.25)
set(handle.food(:),'string','');
for i = find(food)'
set(handle.food(:,i),'string',num2str(food(i)))
end
topScent = max([redScent(:); blackScent(:); 1]);
topDeath = max([redDeath(:); blackDeath(:); 1]);
set([handle.axisrd handle.axisbd], 'clim',[0 topDeath])
set(handle.imagers,'Cdata',redScent)
set(handle.imagebs,'Cdata',blackScent)
set(handle.imagerd,'Cdata',redDeath)
set(handle.imagebd,'Cdata',blackDeath)
axes(handle.axisrd)
title(['Death Red' char(10) 'with ' num2str(sum(red(:))) ' red ants alive at time ' num2str(timeStep) '.'])
axes(handle.axisbd)
title(['Death Black' char(10) 'with ' num2str(sum(black(:))) ' black ants alive at time ' num2str(timeStep) '.'])
drawnow
end
end
function [blackantsHandle,redantsHandle,foodHandle] = showFeatures(main, food, red, black)
[obsY, obsX] = find(isnan(main));
patch(bsxfun(@plus,obsX,[-0.5 0.5 0.5 -0.5])',bsxfun(@plus,obsY,[0.5 0.5 -0.5 -0.5])','k');
[hy, hx] = find(main == -1); % red anthill
line(hx, hy,'marker','^','color',[1 0 0],'linestyle','none')
[hy, hx] = find(main == 1); % black anthill
line(hx, hy,'marker','^','color',[0 0 0],'linestyle','none')
[sy, sx] = find(ones(size(food))); % sugar
foodHandle = text(sx-.4,sy,'');
for i = find(food)'
set(foodHandle(i),'string',num2str(food(i)))
end
[xx,yy]=getSpots(black);
blackantsHandle = line(xx,yy+.25,'marker','.','color','k','linestyle','none');
[xx,yy]=getSpots(red);
redantsHandle = line(xx,yy-.25,'marker','.','color','r','linestyle','none');
numRows = size(main,1);
numCols = size(main,2);
line(repmat([0;numCols+1]-0.5,1,numRows+1),[0:numRows;0:numRows]+.5,'color',[0.8 0.8 0.8])
line([0:numCols;0:numCols]+.5,repmat([0;numRows+1]-0.5,1,numCols+1),'color',[0.8 0.8 0.8])
end
function [xx,yy] = getSpots(A)
[q,r] = find(A);
m = nonzeros(A);
if isempty(m)
xx=[];yy=[];
return
end
n = sum(m);
o = cumsum([0;m(1:end-1)])+1;
u = zeros(n,1);u(o) = 1;
v = zeros(n,1);v(o) = -[0;m(1:end-1)];
w = v; w(o) = w(o)+m;
u = cumsum(u);
v = cumsum(1+v)-1;
w = cumsum(w)-1;
xx = r(u)+min(.8./w,1).*v+.1+(w==0).*.4-.5;
yy = q(u);
end
function mergeList = getMergeList(red, black)
redList = antMat2List( red);
blackList = antMat2List(black);
mergeList = lists2mergeList(redList, blackList);
end
function list = antMat2List(inMat)
if nnz(inMat) == 0;
list = [];
return
end
numAnts = sum(inMat(:));
list(numAnts).r = []; %preallocate
for i = 1:numAnts
[r,c] = find(inMat,1);
list(i).r = r;
list(i).c = c;
inMat(r,c) = inMat(r,c) - 1;
end
end
function mergeList = lists2mergeList(redList, blackList)
finished = 0;
i = 1;
totalNum = numel(redList) + numel(blackList);
if totalNum == 0
mergeList = nan;
return
end
mergeList(totalNum).team = []; %preallocate
mergeList(totalNum).r = []; %preallocate
mergeList(totalNum).c = []; %preallocate
ri = 1;
bi = 1;
rMax = numel( redList);
bMax = numel(blackList);
while ~finished
if ri <= rMax
mergeList(i).team = 'r';
mergeList(i).r = redList(ri).r;
mergeList(i).c = redList(ri).c;
ri = ri + 1;
i = i+1;
end
if bi <= bMax
mergeList(i).team = 'b';
mergeList(i).r = blackList(bi).r;
mergeList(i).c = blackList(bi).c;
bi = bi + 1;
i = i+1;
end
if ((ri > rMax) && (bi > bMax))
finished = 1;
end
end
end
function actions = safeCheckAction(actions)
if isempty(actions.dRow) || ...
isempty(actions.dCol) || ...
isempty(actions.action)
actions.dRow = 0;
actions.dCol = 0;
actions.action = 0;
return
end
if ~ismember(actions.dRow , [-1 0 1]) || ...
~ismember(actions.dCol , [-1 0 1]) || ...
~ismember(actions.action, [-1 0 1])
actions.dRow = 0;
actions.dCol = 0;
actions.action = 0;
end
end