--[[
Cpu logic for Company
Copyright Motuk 2011.
License: No specific license. Code may be used for non-commercial purposes.
                        
 ######## VERSION HISTORY ##########
 # v0.1 05/21/2011 by Motuk        #
 # * initial release for           #
 #   PSP Genesis Competition 2011  #
 # v1.0 07/22/2011 by Motuk        #
 # * Release for NEO Compo 2011    #
 ###################################
 --]]
 
--[[
   Returns card index that is given to other player when Deal-card is played
  ]]
cpuSelectCard = function(index, who)
   local currentHand = _G["hand"..who];
   local currentDif = getDifficulty(who);
   local targetCard = index; -- Must be changed before returning
   
   -- Good choises first, if difficulty is high enough
   -- Othervice fall back to worse options, if others are not possible
   if(currentDif>2) then
      -- Give "bad" card away
      -- Tries to cause  most negative impact to others
      
      -- Get rid of negative cards in this order
      -- Give away money cards only if game is ending
      targetCard = arrayContainsCard(currentHand,"Bankruptcy");
      if(targetCard > 0 and targetCard ~= index) then
         return targetCard;
      end
      targetCard = arrayContainsCard(currentHand,"$100000");
      if(targetCard > 0 and targetCard ~= index and gameIsEnding(who)) then
         return targetCard;
      end
      targetCard = arrayContainsCard(currentHand,"Deal");
      if(targetCard > 0 and targetCard ~= index) then
         return targetCard;
      end
      targetCard = arrayContainsCard(currentHand,"$50000");
      if(targetCard > 0 and targetCard ~= index and gameIsEnding(who)) then
         return targetCard;
      end
      targetCard = arrayContainsCard(currentHand,"Robbery");
      if(targetCard > 0 and targetCard ~= index) then
         return targetCard;
      end
      targetCard = arrayContainsCard(currentHand,"$25000");
      if(targetCard > 0 and targetCard ~= index and gameIsEnding(who)) then
         return targetCard;
      end
      targetCard = arrayContainsCard(currentHand,"$15000");
      if(targetCard > 0 and targetCard ~= index and gameIsEnding(who)) then
         return targetCard;
      end
   end
   
   -- Shuffle hand so cards are picked in random order
   local shuffledHand = nil;
   local indexes = nil;
   shuffledHand, indexes = getShuffledArray(currentHand,maxHandSize);
   
   if(currentDif>1 or (currentDif == 1 and math.random(1,2) == 1)) then
      -- Give random card that can't be played
      -- Also,  easy player has 50% chance to skip this and give totally random card
      targetCard = 0;
      while (targetCard < maxHandSize) do
         targetCard = targetCard + 1
         if(shuffledHand[targetCard] ~= nil) then
            local playCardTarget = decideTarget(shuffledHand[targetCard],who);
            if(indexes[targetCard] ~= index and not cardCanBePlayed(shuffledHand[targetCard],who,playCardTarget)) then
               if(currentDif == 1 or not keepCardForFuture(shuffledHand[targetCard],who)) then
                  -- Medium and hard players don't give away cards that give advantage to others
                  return indexes[targetCard];
               end
            end
         end
      end
   end
   
   -- Give random card (50% for easy player or when all cards are playable)
   targetCard = index;
   while(targetCard == index) do
      targetCard = math.random(1,maxHandSize);
   end
   return targetCard;
end

--[[
   Converts text difficuty to number, so it can be used for cumulative option selection
  ]]
getDifficulty = function(who)
   if (difficulty[who-1] == "easy") then
      return 1;
   elseif (difficulty[who-1] == "medium") then
      return 2;
   elseif (difficulty[who-1] == "hard") then
      return 3;
   else
      -- Tweak for human player, which is always hard
      return 3;
   end
end

--[[
   Creates copy of given array and shuffles it to random order
  ]]
getShuffledArray = function(array,size)
   -- There might be nil values when shuffling handcards and it's not
   -- own turn (after deal is played)
   -- Size must be given separetly because #-operator doesn't work with nil values
   -- Nil values are kept in shuffled array for indexes to match
   local indexes = createIndexArray(size);
   local array2 = {}
   for k,v in pairs(array) do
      array2[k] = v
   end
   
	local i = 0;
	while (i < size) do
		i = i + 1;
		local number = math.random(1,size);
      swap(array2,i,number);
      swap(indexes,i,number);
	end
   return array2, indexes
end

--[[
   Creates index array with range 1-size
  ]]
createIndexArray = function(size)
   local indexes = {};
   local i = 0;
	while(i<size) do
		i = i + 1;
      indexes[i] = i;
   end
   return indexes;
end

--[[
   Return true, if given card should remain in hand
   Behaves differently if game is about to end
  ]]
keepCardForFuture = function(cardName,turn)
   local currentDif = getDifficulty(turn);

   -- Keep money and deal unless game is ending soon
   if(cardName == "$15000" or	cardName == "$25000" or
		cardName == "$50000" or cardName == "$100000" or
      cardName == "Deal") then
      return not gameIsEnding(turn);
   end

   -- Never keep bad cards
   if(cardName == "Bankruptcy" or cardName == "Robbery") then
      return false;
   end
   
   -- Give away safe cards if nothing else
   if(cardName == "Remove Conviction" or cardName == "Remove Tax Evasion" or 
      cardName == "Remove Strike" or cardName == "Remove Eco Crime" or
      cardName == "New Identity") then
      -- Hard player keeps safecards if it needs them
      if(currentDif>2) then
         local currentTable = _G["table"..turn];
         -- If hard player has at least one threat, it keeps New Identity
         if(cardName == "New Identity" and #currentTable > 1) then
            -- Open Company + some threat card
            return true;
         end
         local i = 0;
   		while (i < #currentTable) do
   			i = i + 1;
   			if (currentTable[i] == convertSafeToThreat(cardName)) then
   				return true;
   			end
   		end
      end
      return false;
	end
   
   -- Hard player discards open company if no bankruptcy in hand, others keep always
   if(cardName == "Open Company") then
      local currentHand = _G["hand"..turn];
      if(currentDif>2) then
         local targetCard = arrayContainsCard(currentHand,"Bankruptcy");
         if(targetCard > 0) then
            return false;
         end
      end
      -- If multiple cards, then don't keep
      if(arrayContainsCard(currentHand,cardName,false,false) ~=
         arrayContainsCard(currentHand,cardName,false,true)) then
         return false;
      end
      return true;
   end
   
   -- If multiple cards, then don't keep these
   if(cardName == "Sell Company" or	cardName == "Small Safe" or
		cardName == "Big Safe" or cardName == "Bribe" or
      cardName == "New Beginning" or cardName == "Conviction" or
      cardName == "Tax Evasion" or cardName == "Strike" or
      cardName == "Eco Crime" or
      cardName == "Rotate Table") then
      local currentHand = _G["hand"..turn];
      if(arrayContainsCard(currentHand,cardName,false,false) ~=
         arrayContainsCard(currentHand,cardName,false,true)) then
         return false;
      end
      return true;
   end
   
   -- Always keep these (the rest)
   if(cardName == "Thief" or cardName == "Banker" or cardName == "Luck" or
      cardName == "Sabotage" or cardName == "Rotate Hands") then
      return true;
   end
end

--[[
   Calculates how many turns it takes to get rid of all negative cards
  ]]
gameIsEnding = function(turn)
   -- For easy player game is ending when 3/4 of deck is used
   local aboutToEnd = (deckIndex > (#deck - #deck/4));
   
   local currentDif = getDifficulty(turn);
   local currentHand = _G["hand"..turn];
   
   -- Hard and medium players counts how many turns to get rid of bad cards + delta
   -- Delta is now same as bad cards
   if(currentDif>1) then
      local badCards = 0;
      local i = 0;
      while (i < maxHandSize) do
         i = i + 1
         if(currentHand[i] == "Bankruptcy" or currentHand[i] == "Robbery" or
            currentHand[i] == "Deal" or currentHand[i] == "$100000" or
            currentHand[i] == "$50000" or currentHand[i] == "$25000" or
            currentHand[i] == "$15000") then
            badCards = badCards + 1;
         end
      end
      -- One round takes (normally) as many cards as there are players and x2 is for safety (new beginning etc.)
      aboutToEnd = (deckIndex > (#deck - 2*playersSize*badCards));
   end
   
   -- TODO: Hard players can count if all sell company cards are played?
   return aboutToEnd;
end

--[[ 
This function is to determine logic which card cpu player should play.
In addition, the legality of playing the card must be checked in this
(or sub)function.
PARAMETERS
 * Turn of player [2-4]
RETURNS
 * playCardIndex  : Card Index in hand that is wanted to be played or discarded [1-7]
 * playCardTarget : Target of the played card [1-4]. If given value is 0, then discards 
                    a card from hand that correspond to playCardIndex ]]

cpu = function(turn)
   local playCardIndex = 1;
	local playCardTarget = 0;
	
   -- Sort cards in hand
   local currentHand = _G["hand"..turn];
   local sortedHand = nil;
   local indexes = nil;
   sortedHand, indexes = getPrioritizedHand(currentHand,maxHandSize,turn);
   
   local i = 0;
   while (i < maxHandSize) do
      i = i + 1
      
      if(sortedHand[i] == nil) then
         --printCardData(sortedHand,maxHandSize,turn);
         --printCardData(indexes,maxHandSize,turn);
      else
         playCardTarget = decideTarget(sortedHand[i],turn);
         
         -- Target can be set to 0 if card needs to be discarded
         if(playCardTarget==0) then
            return indexes[i],0;
         end
         
         if (cardCanBePlayed(sortedHand[i],turn,playCardTarget)) then
            playCardIndex = indexes[i];
            break;
         else
            playCardTarget = 0;
         end
      end
   end
   
   -- Discard card if nothing else can be done
   if( playCardTarget == 0) then
      playCardIndex = chooseCardToBeDiscarded(turn);
   end

	return playCardIndex, playCardTarget;
end

--[[
   Creates copy of hand cards and sorts them by priority
  ]]
getPrioritizedHand = function(hand,size,turn)
   -- When calling this, hand is always full and there are no nil items
   -- Despite of that take size from other parameter
   local indexes = createIndexArray(size);
   local sortedHand = {}
   for k,v in pairs(hand) do
      -- Cause error if nil values, because rest of the function doesn't work then
      if(v == nil) then
         --error("Nil card in hand when prioritizing it"); 
         -- Release version: Don't crash game, but return unsorted hand
         return hand, indexes;
      end
      sortedHand[k] = v;
   end
   
   -- Cards are checked in priority order and then swapped to current index,
   -- and finally we have sorted table
   -- Main priority can change if other cards are in hand an diffuculty is high enough
   -- In that case higher priority cards move lower priority cards before them
   local currentDif = getDifficulty(turn);
   local currentCompany = _G["company"..turn];
   local currentTable = _G["table"..turn];
   local cardIndex = 0;
   
   -- Order money  suffling order from smaller to bigger
   local money = {"$15000","$25000","$50000","$100000"};
   -- But easy player randomly
   if(currentDif == 1) then
      local tempIndex = nil;
      money, tempIndex = getShuffledArray(money,#money);
   end
   local moneyIndex = 1;
   
   local currentIndex = 0
	while (currentIndex < size) do
		currentIndex = currentIndex + 1;
      local skip = false;
      
      -- Open company if possible
      cardIndex = arrayContainsCard(sortedHand,"Open Company");
      if(not skip and cardIndex>currentIndex) then
         if(currentDif > 2) then
            -- Hard player plays bankcrupcy first
            local otherIndex = arrayContainsCard(sortedHand,"Bankruptcy");
            if(otherIndex>currentIndex) then -- Don't swap cards that are processed already
               swap(sortedHand,currentIndex,otherIndex);
               swap(indexes,currentIndex,otherIndex);
               currentIndex = currentIndex + 1; -- Main card goes to next index
            end
         end
         
         swap(sortedHand,currentIndex,cardIndex);
         swap(indexes,currentIndex,cardIndex);
         skip = true;
      end
      
      -- Must get rid of bankruptcy
      cardIndex = arrayContainsCard(sortedHand,"Bankruptcy");
      if(not skip and cardIndex>currentIndex) then
         swap(sortedHand,currentIndex,cardIndex);
         swap(indexes,currentIndex,cardIndex);
         skip = true;
      end
      
      -- Try to get rid of deal
      cardIndex = arrayContainsCard(sortedHand,"Deal");
      if(not skip and cardIndex>currentIndex) then
         swap(sortedHand,currentIndex,cardIndex);
         swap(indexes,currentIndex,cardIndex);
         skip = true;
      end
      
      -- Try to get rid of robbery
      cardIndex = arrayContainsCard(sortedHand,"Robbery");
      if(not skip and cardIndex>currentIndex) then
         swap(sortedHand,currentIndex,cardIndex);
         swap(indexes,currentIndex,cardIndex);
         skip = true;
      end
      
      -- Luck after negative cards
      cardIndex = arrayContainsCard(sortedHand,"Luck");
      if(not skip and cardIndex>currentIndex) then
         if(currentDif > 2) then
            -- Hard plays only if bank is ready
            local otherIndex = arrayContainsCard(sortedHand,"Big Safe");
            if(otherIndex==0) then otherIndex = arrayContainsCard(sortedHand,"Small Safe"); end
            if(otherIndex>currentIndex) then
               swap(sortedHand,currentIndex,otherIndex);
               swap(indexes,currentIndex,otherIndex);
               currentIndex = currentIndex + 1; -- Main card goes to next index
            end
         end
         swap(sortedHand,currentIndex,cardIndex);
         swap(indexes,currentIndex,cardIndex);
         skip = true;
      end
      
      if(not skip and moneyIndex <= #money) then
         cardIndex = arrayContainsCard(sortedHand,money[moneyIndex]);
         if(cardIndex>currentIndex) then
            if(currentDif > 1) then
               
               if(sortedHand[cardIndex] == "$100000" or sortedHand[cardIndex] == "$50000") then
                  -- Medium and hard players play bank before big money if possible
                  local otherIndex = arrayContainsCard(sortedHand,"Big Safe");
                  if(otherIndex==0 or (otherIndex>0 and arrayContainsCard(sortedHand,"$50000")==0)) then
                     otherIndex = arrayContainsCard(sortedHand,"Small Safe");
                  end
                  
                  -- Move bank before this money card
                  if(otherIndex>currentIndex) then
                     swap(sortedHand,currentIndex,otherIndex);
                     swap(indexes,currentIndex,otherIndex);
                     currentIndex = currentIndex + 1; -- Main card goes to next index
                  end
               else
                  -- Hard and medium players can tease others with 33% chance, if no money at company
                  local points = {calculateCompanyPoints()};
                  if(points[turn] == 0 and math.random(1,3) == 1) then
                     local otherIndex = arrayContainsCard(sortedHand,"Eco Crime");
                     if(otherIndex==0) then otherIndex = arrayContainsCard(sortedHand,"Strike"); end
                     if(otherIndex==0) then otherIndex = arrayContainsCard(sortedHand,"Tax Evasion"); end
                     if(otherIndex==0) then otherIndex = arrayContainsCard(sortedHand,"Conviction"); end
                     
                     if(otherIndex>currentIndex) then
                        swap(sortedHand,currentIndex,otherIndex);
                        swap(indexes,currentIndex,otherIndex);
                        currentIndex = currentIndex + 1; -- Main card goes to next index
                     end
                  end
               end
            end
            
            -- TODO?: hard skips 50k and 100k if luck cards in play and not about to end
            swap(sortedHand,currentIndex,cardIndex);
            swap(indexes,currentIndex,cardIndex);
         elseif(cardIndex<currentIndex) then
            -- We need to decrease currentIndex, because we skip other cards until all money
            -- are ordered and at start of loop index is increased
            -- Alse current money wasn't found so we didn't swap any cards
            currentIndex = currentIndex - 1;
            
            -- Increase money index only when no current money was found
            -- This enables sorting of multiple same kind of money cards
            moneyIndex = moneyIndex + 1
         end
         -- Skip after every single money card
         skip = true;
      end
      
      -- Thief before banks
      cardIndex = arrayContainsCard(sortedHand,"Thief");
      if(not skip and cardIndex>currentIndex) then
         if(currentDif > 1) then
            -- Medium and hard bank first if much money at company
            -- Also avoid big bank if small bank is sufficient
            local otherIndex = arrayContainsCard(sortedHand,"Big Safe");
            if(otherIndex==0 or (otherIndex>0 and arrayContainsCard(sortedHand,"$50000")==0)) then
               otherIndex = arrayContainsCard(sortedHand,"Small Safe");
            end
            -- How much is  much money?
            local points = {calculateCompanyPoints()};
            -- Don't count 100k card because it cannot be banked
            local sub = 0;
            local kindex = 0
            while (kindex < #currentCompany) do
               kindex = kindex + 1
               if(currentCompany[kindex] == "$100000") then
                  sub = sub + 100000;
               end
            end
            if(otherIndex>currentIndex and points[turn]-sub>=50000) then
               swap(sortedHand,currentIndex,otherIndex);
               swap(indexes,currentIndex,otherIndex);
               currentIndex = currentIndex + 1; -- Main card goes to next index
            end
         end
         
         swap(sortedHand,currentIndex,cardIndex);
         swap(indexes,currentIndex,cardIndex);
         skip = true;
      end
      
      -- Money to bank if no new money to be played
      -- Medium and hard check that there is money in company (at cardCanBePlayed)
      cardIndex = arrayContainsCard(sortedHand,"Small Safe");
      if(not skip and cardIndex>currentIndex) then
         swap(sortedHand,currentIndex,cardIndex);
         swap(indexes,currentIndex,cardIndex);
         skip = true;
      end
      cardIndex = arrayContainsCard(sortedHand,"Big Safe");
      if(not skip and cardIndex>currentIndex) then
         swap(sortedHand,currentIndex,cardIndex);
         swap(indexes,currentIndex,cardIndex);
         skip = true;
      end
      
      -- Rotate Table after safes so our money is is not rotated
      cardIndex = arrayContainsCard(sortedHand,"Rotate Table");
      if(not skip and cardIndex>currentIndex) then
         swap(sortedHand,currentIndex,cardIndex);
         swap(indexes,currentIndex,cardIndex);
         skip = true;
      end
      
      -- Threat counter cards before sell company
      -- Low priority, if player is under threat effects he cannot play other cards so these get played
      cardIndex = arrayContainsCard(sortedHand,"Remove Conviction");
      if(not skip and cardIndex>currentIndex) then
         -- If we have multiple threats, try to use New Identity instead of single removal cards
         -- TODO: Does difficulty affect?
         if(#currentTable > 2) then
            local otherIndex = arrayContainsCard(sortedHand,"New Identity");
            if(otherIndex>currentIndex) then -- Don't swap cards that are processed already
               swap(sortedHand,currentIndex,otherIndex);
               swap(indexes,currentIndex,otherIndex);
               currentIndex = currentIndex + 1; -- Main card goes to next index
            end
         end
         swap(sortedHand,currentIndex,cardIndex);
         swap(indexes,currentIndex,cardIndex);
         skip = true;
      end
      cardIndex = arrayContainsCard(sortedHand,"Remove Tax Evasion");
      if(not skip and cardIndex>currentIndex) then
         -- If we have multiple threats, try to use New Identity instead of single removal cards
         -- TODO: Does difficulty affect?
         if(#currentTable > 2) then
            local otherIndex = arrayContainsCard(sortedHand,"New Identity");
            if(otherIndex>currentIndex) then -- Don't swap cards that are processed already
               swap(sortedHand,currentIndex,otherIndex);
               swap(indexes,currentIndex,otherIndex);
               currentIndex = currentIndex + 1; -- Main card goes to next index
            end
         end
         swap(sortedHand,currentIndex,cardIndex);
         swap(indexes,currentIndex,cardIndex);
         skip = true;
      end
      cardIndex = arrayContainsCard(sortedHand,"Remove Strike");
      if(not skip and cardIndex>currentIndex) then
         -- If we have multiple threats, try to use New Identity instead of single removal cards
         -- TODO: Does difficulty affect?
         if(#currentTable > 2) then
            local otherIndex = arrayContainsCard(sortedHand,"New Identity");
            if(otherIndex>currentIndex) then -- Don't swap cards that are processed already
               swap(sortedHand,currentIndex,otherIndex);
               swap(indexes,currentIndex,otherIndex);
               currentIndex = currentIndex + 1; -- Main card goes to next index
            end
         end
         swap(sortedHand,currentIndex,cardIndex);
         swap(indexes,currentIndex,cardIndex);
         skip = true;
      end
      cardIndex = arrayContainsCard(sortedHand,"Remove Eco Crime");
      if(not skip and cardIndex>currentIndex) then
         -- If we have multiple threats, try to use New Identity instead of single removal cards
         -- TODO: Does difficulty affect?
         if(#currentTable > 2) then
            local otherIndex = arrayContainsCard(sortedHand,"New Identity");
            if(otherIndex>currentIndex) then -- Don't swap cards that are processed already
               swap(sortedHand,currentIndex,otherIndex);
               swap(indexes,currentIndex,otherIndex);
               currentIndex = currentIndex + 1; -- Main card goes to next index
            end
         end
         swap(sortedHand,currentIndex,cardIndex);
         swap(indexes,currentIndex,cardIndex);
         skip = true;
      end
      
      -- New Identity after other threat removals so they are used when only one threat
      cardIndex = arrayContainsCard(sortedHand,"New Identity");
      if(not skip and cardIndex>currentIndex) then
         swap(sortedHand,currentIndex,cardIndex);
         swap(indexes,currentIndex,cardIndex);
         skip = true;
      end
      
      -- Use bribe last, if no other counter cards are available
      cardIndex = arrayContainsCard(sortedHand,"Bribe");
      if(not skip and cardIndex>currentIndex) then
         swap(sortedHand,currentIndex,cardIndex);
         swap(indexes,currentIndex,cardIndex);
         skip = true;
      end
      
      -- Sell company has low priority
      -- After all money and bank cards are played + threat removals
      -- Hard player plays this only when it is winning (money in company and bank highest)(at cardCanBePlayed)
      cardIndex = arrayContainsCard(sortedHand,"Sell Company");
      if(not skip and cardIndex>currentIndex) then
         swap(sortedHand,currentIndex,cardIndex);
         swap(indexes,currentIndex,cardIndex);
         skip = true;
      end
      
      --  New beginning before tease cards
      -- Rotate Hands has same function as New Beginning (for now?)
      cardIndex = arrayContainsCard(sortedHand,"Rotate Hands");
      if(cardIndex<=currentIndex) then
         cardIndex = arrayContainsCard(sortedHand,"New Beginning");
      end
      if(not skip and cardIndex>currentIndex) then
         -- Hard and medium plays only when game is not about to end
         -- and when banker is not in hand (at cardCanBePlayed)
         swap(sortedHand,currentIndex,cardIndex);
         swap(indexes,currentIndex,cardIndex);
         
         -- Medium and hard try to play new beginning first if multiple negative cards in hand 
         if(currentDif > 1) then
            local badCards = 0;
            local i = 0;
            while (i < size) do
               i = i + 1
               if(sortedHand[i] == "Bankruptcy" or
                  sortedHand[i] == "Deal" or
                  sortedHand[i] == "Robbery") then
                  badCards = badCards + 1;
               end
            end
            if(badCards>2) then
               -- Move new beginning to first index
               swap(sortedHand,currentIndex,1);
               swap(indexes,currentIndex,1);
            end
         end

         skip = true;
      end
      
      -- Tease others if no cards to help self
      cardIndex = arrayContainsCard(sortedHand,"Sabotage");
      if(not skip and cardIndex>currentIndex) then
         swap(sortedHand,currentIndex,cardIndex);
         swap(indexes,currentIndex,cardIndex);
         skip = true;
      end
      cardIndex = arrayContainsCard(sortedHand,"Conviction");
      if(not skip and cardIndex>currentIndex) then
         swap(sortedHand,currentIndex,cardIndex);
         swap(indexes,currentIndex,cardIndex);
         skip = true;
      end
      cardIndex = arrayContainsCard(sortedHand,"Tax Evasion");
      if(not skip and cardIndex>currentIndex) then
         swap(sortedHand,currentIndex,cardIndex);
         swap(indexes,currentIndex,cardIndex);
         skip = true;
      end
      cardIndex = arrayContainsCard(sortedHand,"Strike");
      if(not skip and cardIndex>currentIndex) then
         swap(sortedHand,currentIndex,cardIndex);
         swap(indexes,currentIndex,cardIndex);
         skip = true;
      end
      cardIndex = arrayContainsCard(sortedHand,"Eco Crime");
      if(not skip and cardIndex>currentIndex) then
         swap(sortedHand,currentIndex,cardIndex);
         swap(indexes,currentIndex,cardIndex);
         skip = true;
      end
      
      -- Banker can't be played so it can be last
      cardIndex = arrayContainsCard(sortedHand,"Banker");
      if(not skip and cardIndex>currentIndex) then
         swap(sortedHand,currentIndex,cardIndex);
         swap(indexes,currentIndex,cardIndex);
         skip = true;
      end
	end
      
   return sortedHand, indexes;
end

--[[
   Swaps items at indexes i and j in array
  ]]
swap = function(array,i,j)
   array[i], array[j] = array[j], array[i]
end

--[[
   Checks if given card is legal to play
  ]]
cardCanBePlayed = function(cardName,turn,target)
   -- Move must be legal when this function is called
   
   -- If target is set to 0, don't let card be played because it's set to be discarded
   -- That way in deal these (bad)cards go to other players
   if(target==0 or target==nil) then
      return false;
   end
   
   -- This function also includes some random behavior on lower difficulties,
   local currentDif = getDifficulty(turn);
   local currentHand = _G["hand"..turn];
   local currentTable = _G["table"..target];
   local currentCompany = _G["company"..target];

   -- Open company only when it has not been played
   if (cardName == "Open Company") then
		local i = 0;
		while (i < #currentTable) do
			i = i + 1
			if (currentTable[i] == "Open Company") then
				return false;
			end
		end
      
   -- Money, when there is company and no threats
   elseif (cardName == "$15000" or cardName == "$25000" or cardName == "$50000" or cardName == "$100000" or cardName == "Small Safe" or cardName == "Big Safe") then
      if (not checkCompany(target) or threatCompany(target)) then
         return false;
      end
      if(currentDif == 1 and math.random(1,3) == 1) then
         -- Easy player has 33% chance to skip these
         return false;
      end
      
      -- Hard player plays big money only if big bank or game is ending
      if(currentDif > 1 and cardName == "$50000" and not gameIsEnding(turn)) then
         local points = {calculateCompanyPoints()};
         -- Don't count 100k card
         local sub = 0;
         local kindex = 0
         while (kindex < #currentCompany) do
            kindex = kindex + 1
            if(currentCompany[kindex] == "$100000") then
               sub = sub + 100000;
            end
         end
         if(points[target]-sub>0) then
            if(cardName == "Big Safe" and arrayContainsCard(currentHand,"$50000")==0 and arrayContainsCard(currentHand,"Small Safe")>0) then
               -- Don't use big safe if there is small which can hold all money
               return false;
            end
            return true;
         end
         return false;
      end
      
       -- Medium and hard check that there is money in company before playing banks
      if(currentDif > 1 and (cardName == "Small Safe" or cardName == "Big Safe")) then
         local points = {calculateCompanyPoints()};
         -- Don't count 100k card
         local sub = 0;
         local kindex = 0
         while (kindex < #currentCompany) do
            kindex = kindex + 1
            if(currentCompany[kindex] == "$100000") then
               sub = sub + 100000;
            end
         end
         if(points[target]-sub>0) then
            if(cardName == "Big Safe" and arrayContainsCard(currentHand,"$50000")==0 and arrayContainsCard(currentHand,"Small Safe")>0) then
               -- Don't use big safe if there is small which can hold all money
               return false;
            end
            return true;
         end
         return false;
      end
      
   -- Sell company, when enough money
	elseif (cardName == "Sell Company") then
		if (not checkCompany(target) or threatCompany(target) or not enoughMoneyForSelling(target)) then
         return false;
      end
      if(currentDif == 1) then
         -- Easy player doesn't sell company ever
         return false;
      end
      -- Medium ans hard player plays this only when it is winning (money in company and bank highest)
      if(currentDif > 1) then
         local points = {calculateCompanyPoints()};
         local bestPlayer = 0
         local bestMoney = 0
         
         local j = 0;
         while (j < #points) do
            j = j + 1
            local currentBank = _G["bank"..j];
            local currentMoney = points[j]+currentBank;
            if(currentMoney == 0 or currentMoney > bestMoney) then
               bestPlayer = j;
               bestMoney = currentMoney;
            end
         end
         if(bestPlayer ~= turn) then
            -- We are not the best player-> don't play
            return false;
         end
      end

   -- All threads when target has no current threat
	elseif (cardName == "Conviction" or cardName == "Tax Evasion" or cardName == "Strike" or cardName == "Eco Crime") then
		if (not checkCompany(target) or target == turn) then
         -- Target is set to self in some cases when player doesn't want to play it
         return false;
      end
      
		local i = 0;
		while (i < #currentTable) do
			i = i + 1;
			if (currentTable[i] == cardName) then
				return false;
			elseif (userOptions[2] == "no" and (currentTable[i] == "Conviction" or currentTable[i] == "Tax Evasion" or currentTable[i] == "Strike" or currentTable[i] == "Eco Crime")) then
				-- If only one threat is allowed and it is already at table, don' play new card
            return false;
			end
		end
      
   -- Sabotage almost like normal threat card
	elseif (cardName == "Sabotage") then
		if (not checkCompany(target) or target == turn) then
         -- Target is set to self in some cases when player doesn't want to play it
         return false;
      end
      
      -- Hard+Medium play only when no threat cards or multiple threats are allowed (so cards possibilities arent wasted)
      if(currentDif > 1) then
         if((userOptions[2] == "yes" and #currentTable == 5) or
            (userOptions[2] == "no" and #currentTable == 2)) then
            return false;
         end
		end
      
   -- All safe card can be played if target has current threat
	elseif (cardName == "Remove Conviction" or cardName == "Remove Tax Evasion" or
           cardName == "Remove Strike" or cardName == "Remove Eco Crime") then
		if (not checkCompany(target)) then
         return false;
      end
      
      local foundThreat = false;
      local i = 0;
		while (i < #currentTable) do
			i = i + 1;
			if (currentTable[i] == convertSafeToThreat(cardName)) then
				foundThreat = true;
				break;
			end
		end
      return foundThreat;
      
   -- New Identity when at least one threat card
   -- (normal threat removal cards prioritized before this)
	elseif (cardName == "New Identity") then
		if (not checkCompany(target)) then
         return false;
      end
      
      if(#currentTable <= 1) then
         return false;
      end
      
   -- Bribe only when under threat and money to bribe
	elseif (cardName == "Bribe") then
      if (not checkCompany(target) or (not threatCompany(target)) or (not checkMoney(target))) then
         return false;
      end
      
   -- Thief and Luck, when player has company and target has company
   -- Note: Luck doesn't require company, but computer plays it only with company
   elseif (cardName == "Thief" or cardName == "Luck") then
      if (not checkCompany(turn) or not checkCompany(target) or target == turn) then
         return false;
      end

   -- Hard and medium plays only when game is not about to end
   -- and when banker is not in hand
	elseif (cardName == "New Beginning") then
      if(currentDif > 1) then
         if(gameIsEnding(turn) or arrayContainsCard(currentHand,"Banker")) then
            -- New beginning might have been moved to first index, and then it should be played always
            if(currentHand[1] ~= "New Beginning") then
               return false;
            end
         end
      end
   
   -- Medium plays when no money in company
   -- Hard plays only if losses are smaller than without playing (-25k)
	elseif (cardName == "Robbery") then
      local points = {calculateCompanyPoints()};
      if(currentDif == 2 and points[turn] > 0) then
         return false;
      end
      if(currentDif == 3) then
         if(points[turn] > 0 and arrayContainsCard(currentTable,"$15000",true)==0) then
            return false;
         end
      end
   
   -- Medium plays only when company is empty or about to end 
   -- Hard plays when losses smaller than 50k (TODO:or bankruptcies are gone)
	elseif (cardName == "Deal") then
      local points = {calculateCompanyPoints()};
      if(currentDif == 2 and (points[turn] > 0 or not gameIsEnding(turn))) then
         return false;
      end
      if(currentDif == 3) then
         if(points[turn] > 0 and
            (arrayContainsCard(currentTable,"$100000",true)>0 or
             arrayContainsCard(currentTable,"$50000",true)>0)) then
            return false;
         end
      end
      
   -- Medium and hard plays when little money at company+bank
	elseif (cardName == "Bankruptcy") then
      local points = {calculateCompanyPoints()};
      local currentBank = _G["bank"..target];
      
      if(currentDif > 1 and points[turn]+currentBank > 50000) then
         return false;
      end
      
   -- These are always playable, but if target is se to self, computer doesn't want to play them (yet)
   elseif (cardName == "Rotate Table" or cardName == "Rotate Hands") then
      if(target == turn) then
         return false;
      end
      -- But don't play Rotate Hands if we have banker only hard
      if(currentDif > 2 and cardName == "Rotate Hands" and arrayContainsCard(currentHand,"Banker")) then
         return false;
      end
      
   -- Banker is not playable
   elseif (cardName == "Banker") then
		return false;
   end
   
   return true;
end

--[[
   Gives target to card
  ]]
decideTarget = function(cardName,turn)
   local currentDif = getDifficulty(turn);

   if(cardName == "Open Company" or cardName == "Sell Company" or
		cardName == "Small Safe" or cardName == "Big Safe" or	cardName == "Bribe" or
		cardName == "Bankruptcy" or cardName == "New Beginning" or cardName == "Robbery" or
      cardName == "New Identity") then
		return turn;
   elseif(cardName == "$15000" or cardName == "$25000" or
          cardName == "$50000" or cardName == "$100000") then
      -- Hard and medium:If moneycards can't be played and game is about to end, discard them
      if(currentDif > 1) then
         if(not cardCanBePlayed(cardName,turn,turn) and gameIsEnding(turn)) then
            -- Money can't be played because threats, see if we can counter any threat
            local currentHand = _G["hand"..turn];
            local currentTable = _G["table"..turn];
            local i = 0;
            while (i < maxHandSize) do
               i = i + 1
               if(currentHand[i] ~= nil and arrayContainsCard(currentTable,convertSafeToThreat(currentHand[i]),true)>0) then
                  -- We have counter card -> play it first 
                  -- Bribe cards not counted because game is ending and it could give advantage to others
                  --return self and cardCanBePlayed won't let this moneycard be played)
                  return turn;
               end
            end
            -- No suitable counter card -> discard money
            return 0;
         end
      end
      return turn;
   elseif (cardName == "Banker") then
		-- Cannot be played so we return self
      return turn;
   elseif (cardName == "Thief") then
		return calculateThiefTarget(cardName,turn);
	elseif (cardName == "Conviction" or cardName == "Tax Evasion" or
           cardName == "Strike" or cardName == "Eco Crime") then
		return calculateThreatTarget(cardName,turn);
	elseif (cardName == "Deal") then
		return calculateDealTarget(cardName,turn);
	elseif (cardName == "Luck") then
		return calculateLuckTarget(cardName,turn);
   elseif (cardName == "Remove Conviction" or cardName == "Remove Tax Evasion" or
           cardName == "Remove Strike" or cardName == "Remove Eco Crime") then
      return calculateSafeTarget(cardName,turn);
   elseif (cardName == "Rotate Table") then
		return calculateRotateTableTarget(cardName,turn);
	elseif (cardName == "Rotate Hands") then
		return calculateRotateHandsTarget(cardName,turn);
	elseif (cardName == "Sabotage") then
		return calculateSabotageTarget(cardName,turn);
	end
end

--[[
   Returns player, to whom thief is used  
  ]]
calculateThiefTarget = function(cardName,turn)
   local currentDif = getDifficulty(turn);
   local target = turn;
   
   --Hard and medium player targets the one with most money in company
   if(currentDif > 1) then
      local points = {calculateCompanyPoints()};
      local bestPlayer = 0
      local j = 0;
      while (j < #points) do
         j = j + 1
         if((j ~= turn) and (bestPlayer == 0 or points[j] > points[bestPlayer])) then
            bestPlayer = j;
         end
      end
      if(bestPlayer ~= 0 and points[bestPlayer] > 0) then
         -- Target this if it has money
         target = bestPlayer;
         if(currentDif > 2) then
            -- Hard player doesn't play luck, if target has threats
            -- But plays if only one threat and it can be countered
            local targetTable = _G["table"..target];
            if(#targetTable == 1) then
               -- One table card -> open company, no threats -> keep target
               target = bestPlayer;
            end
            if(#targetTable == 2) then
               -- Open company card and one threat
               local currentHand = _G["hand"..turn];
               if((arrayContainsCard(targetTable,"Conviction",true)>0 and arrayContainsCard(currentHand,"Remove Conviction")>0) or
                  (arrayContainsCard(targetTable,"Tax Evasion",true)>0 and arrayContainsCard(currentHand,"Remove Tax Evasion")>0) or
                  (arrayContainsCard(targetTable,"Strike",true)>0 and arrayContainsCard(currentHand,"Remove Strike")>0) or
                  (arrayContainsCard(targetTable,"Eco Crime",true)>0 and arrayContainsCard(currentHand,"Remove Eco Crime")>0) or
                  (points[turn]>0 and arrayContainsCard(currentHand,"Bribe")>0)) then
                  -- Player can counter target threat with safecard or bribe -> keep target
                  target = bestPlayer;
               else
                  -- Return self so cardCanBePlayed won't let card be played
                  return turn;
               end
            end
            if(#targetTable > 2) then
               -- More than one threat card in play
               return turn;
            end
         end
      else
         -- Don't play card if no-one has money
         return turn;
      end
   end
   
   -- If nothing else, choose random target
   -- Also, easy player has 50% chance not to target human player
   local counter = 0; -- Try random target only 4 times to avoid infinete loops when no valid target
   while target == turn or
         (currentDif == 1 and target == 1 and math.random(1,2) == 1) do
      target = math.random(1,playersSize);
      if(currentDif>1 and not cardCanBePlayed(cardName,turn,target)) then
         -- Medium and hard player target only if thief can surely be played
         target = turn;
      end
      counter = counter + 1;
      if(counter == 4) then
         -- Set to self so card is not played
         target = turn;
         break;
      end
   end
   return target;
end

--[[
   Returns player, to whom sabotage is used  
  ]]
calculateSabotageTarget = function(cardName,turn)
   -- Maybe some other logic, but normal threat logic is good for now
   return calculateThreatTarget(cardName,turn);
end

--[[
   Returns player, to whom threat card is used  
  ]]
calculateThreatTarget = function(cardName,turn)
   local currentDif = getDifficulty(turn);
   local target = turn;
   
   --Hard player targets the one with most money in company
   if(currentDif > 2) then
      local points = {calculateCompanyPoints()};
      local bestPlayer = 0
      local j = 0;
      while (j < #points) do
         j = j + 1
         if((j ~= turn) and (bestPlayer == 0 or points[j] > points[bestPlayer])) then
            bestPlayer = j;
         end
      end
      if(bestPlayer ~= 0 and points[bestPlayer] > 0) then
         -- If target has no money, fall back to random othervice target it
         target = bestPlayer;
      end
   end
   
   -- If nothing else, choose random target
   -- Also, easy player has 50% chance not to target human player
   local counter = 0; -- Try random target only 4 times to avoid infinete loops when no valid target
   while target == turn or
         (currentDif == 1 and target == 1 and math.random(1,2) == 1) do
      -- Note: Random target might not have company yet
      target = math.random(1,playersSize);
      if(currentDif>1 and not cardCanBePlayed(cardName,turn,target)) then
         -- Medium and hard player target only if threat can surely be played
         target = turn;
      end
      counter = counter + 1;
      if(counter == 4) then
         -- Set to self so card is not played
         target = turn;
         break;
      end
   end
   return target;
end

--[[
   Returns player, to whom deal is used  
  ]]
calculateDealTarget = function(cardName,turn)
   -- Deal can be selected at random because we don't know other player cards
   local counter = 0; -- Try random target only 2 times to avoid infinete loops when no valid target
   local target = turn;
   while target == turn do
      target = math.random(1,playersSize);
      counter = counter + 1;
      if(counter == 3) then
         -- Set to human player
         target = 1;
         break;
      end
   end
   return target;
end

--[[
   Returns player, to whom luck is used  
  ]]
calculateLuckTarget = function(cardName,turn)
   local currentDif = getDifficulty(turn);
   local target = turn;
   
   --Hard and medium player targets the one with most money in company
   if(currentDif > 1) then
      local points = {calculateCompanyPoints()};
      local bestPlayer = 0
      local j = 0;
      while (j < #points) do
         j = j + 1
         if((j ~= turn) and (bestPlayer == 0 or points[j] > points[bestPlayer])) then
            bestPlayer = j;
         end
      end
      if(bestPlayer ~= 0 and points[bestPlayer] > 0) then
         -- Target this if it has money
         target = bestPlayer;
         if(currentDif > 2) then
            -- Hard player doesn't play luck, if no bank cards in hand
            local currentHand = _G["hand"..turn];
            if(arrayContainsCard(currentHand,"Small Safe") == 0 and
               arrayContainsCard(currentHand,"Big Safe") == 0) then
               -- Return self so cardCanBePlayed won't let card be played
               return turn;
            end
         end
      else
         -- Don't play card if no-one has money
         return turn;
      end
   end
   
   -- If nothing else, choose random target
   -- Also, easy player has 50% chance not to target human player
   local counter = 0; -- Try random target only 4 times to avoid infinete loops when no valid target
   while target == turn or
         (currentDif == 1 and target == 1 and math.random(1,2) == 1) do
      target = math.random(1,playersSize);
      if(currentDif>1 and not cardCanBePlayed(cardName,turn,target)) then
         -- Medium and hard player target only if luck can surely be played
         target = turn;
      end
      counter = counter + 1;
      if(counter == 4) then
         -- Set to self so card is not played
         target = turn;
         break;
      end
   end
   return target;
end

--[[
   Returns player, to whom safe card is used  
  ]]
calculateSafeTarget = function(cardName,turn)
   local currentDif = getDifficulty(turn);
   if(currentDif < 3) then
      -- Medium and easy players use only to self
      return turn;
   end
   
   -- Hard players can use to others, if there is something to steal from them
   local currentHand = _G["hand"..turn];
   
   if(arrayContainsCard(currentHand,"Thief")==0) then
      -- If not thief card, use only to self
      return turn;
   end
   
   -- Find player with most money on company
   local points = {calculateCompanyPoints()};
   local bestPlayer = 0
   local j = 0;
   while (j < #points) do
      j = j + 1
      if(bestPlayer == 0 or points[j] > points[bestPlayer]) then
         -- Let's make sure that target isn't self and that it has right threat card
         if(j ~= turn) then
            local targetTable = _G["table"..j];
            if(#targetTable <= 2 and
               arrayContainsCard(targetTable,convertSafeToThreat(cardName),true)==0) then
               -- Target has only one threat and we can counter it
               bestPlayer = j;
            end
         end
      end
   end
   
   -- If best player is not found, use only to self
   if(bestPlayer == 0) then
      return turn;
   end
   return bestPlayer;
end

--[[
  Calculates if rotate table should be played or not
  ]]
calculateRotateTableTarget = function(cardName,turn)
   -- Let's see if we got something out of rotating table
   local nextPlayer = turn-1;
   if(nextPlayer==0) then nextPlayer = 4 end -- Kind of modulo operation + it shouldn't be needed
   
   local companyMoney = {calculateCompanyPoints()};
   local currentTableMoney = companyMoney[turn];
   local futureTableMoney = companyMoney[nextPlayer];
   
   -- If other player has more money, switch
   if(futureTableMoney > currentTableMoney) then
      local currentDif = getDifficulty(turn);
      -- Hard player checks for threats
      if(currentDif > 2) then
         local targetTable = _G["table"..nextPlayer];
         local currentHand = _G["hand"..turn];
         if(#targetTable > 2 and arrayContainsCard(currentHand,"New Identity")==0) then
            -- Has atleast one threat  and we have no removal card-> don't use
            return turn;
         end
      end
      return 1 -- Use to human player when we want to play card
   end
   return turn -- Don't play card
end

--[[
   Chooses if rotate hands should be played
  ]]
calculateRotateHandsTarget = function(cardName,turn)
   -- TODO: Use when giving advantage to self
   -- Part of that logic is at getPrioritizedHand, so for now we can always let this card be played
   return 1 -- Use to human player when we want to play card
end

--[[
   Find if given array contains given card
   Starts from end so it can be used for priority sorting
   when there are multiple cards of same type
   If unReverse parameter is given search array from start to end
  ]]
arrayContainsCard = function(array,card,arrayHasNoNils,unReverse)
   -- Third parameter for when using this with other than hand arrays
   local size = 0;
   if(arrayHasNoNils) then
      size = #array;
   else
      -- Parameter is false or nil -> is used with hand cards
      size = maxHandSize;
   end

   local contains = 0;
   if(unReverse) then
      local i = 0;
      while (i < size) do
         i = i + 1
         if(array[i] == card) then
            contains = i;
            break;
         end
      end
   else
      local i = size+1;
      while (i > 1) do
         i = i - 1
         if(array[i] == card) then
            contains = i;
            break;
         end
      end
   end
   return contains;
end

--[[
   Chooses best(worst) card to be discarded
  ]]
chooseCardToBeDiscarded = function(turn)
   local currentHand = _G["hand"..turn];
   local currentDif = getDifficulty(turn);
   local discardIndex = 0;
   
   -- First check if all cards are  non-discardable -> one of them can be discarded
   local badCards = 0;
   local i = 0;
   while (i < maxHandSize) do
      i = i + 1
      if(currentHand[i] == "Bankruptcy" or
         currentHand[i] == "Deal" or
         currentHand[i] == "Robbery") then
         badCards = badCards + 1;
      end
   end
   if(badCards == maxHandSize) then
      --Hard discards worst card
      if(currentDif>2) then
         discardIndex = arrayContainsCard(currentHand,"Bankruptcy");
         if(discardIndex > 0) then return discardIndex; end
         discardIndex = arrayContainsCard(currentHand,"Deal");
         if(discardIndex > 0) then return discardIndex; end
         discardIndex = arrayContainsCard(currentHand,"Robbery");
         if(discardIndex > 0) then return discardIndex; end
      end
      -- Discard one at random
      return math.random(1,maxHandSize);
   end

   loopCount = 0;
   
   repeat
   loopCount = loopCount + 1;
   
   if (loopCount > 500) then
      --[[
      local temp = turn..':Loop'
      printCardData(currentHand,maxHandSize,temp)
      local shuffledHand = nil;
      local indexes = nil;
      shuffledHand, indexes = getShuffledArray(currentHand,maxHandSize);
      printCardData(shuffledHand,maxHandSize,temp)
      printCardData(indexes,maxHandSize,temp)
      error("Looped too much");
      ]]
      -- This shouldn't ever happen (anymore ;)
      -- In any case return last card instead of crashing game
      return maxHandSize;
   end
   
      local shuffledHand = nil;
      local indexes = nil;
      shuffledHand, indexes = getShuffledArray(currentHand,maxHandSize);

      discardIndex = 0;
      if(currentDif>1) then
         -- Easy player always discards at random and skips this
         local keepCardCounter = 0;
         local i = 0;
         while (i < maxHandSize) do
            i = i + 1
            -- Choose first card that we don't need in future from shuffled hand
            if(shuffledHand[i] ~= nil and not keepCardForFuture(shuffledHand[i],turn)) then
               if(discardIndex == 0) then
                  discardIndex = indexes[i];
               end
            else
               keepCardCounter = keepCardCounter + 1;
            end
         end
         -- If too many cards are keeped for future, discard one at random
         if(keepCardCounter >= maxHandSize-badCards) then
            discardIndex = 0;
         end
      end
      
      if(discardIndex == 0 or discardIndex == nil) then
         -- All cards needed, choose random
         discardIndex = math.random(1,maxHandSize);
      end
   until(currentHand[discardIndex] ~= nil and
         currentHand[discardIndex] ~= "Bankruptcy" and
         currentHand[discardIndex] ~= "Deal" and
         currentHand[discardIndex] ~= "Robbery")
   return discardIndex;
end

--[[
   Returns string, which given safeCard can counter
  ]]
convertSafeToThreat = function(safeCard)
   local threat = "";
   -- Make sure that gives string has enough chars
   if(#safeCard>8 and string.sub(safeCard,1,6) == "Remove") then
      threat = string.sub(safeCard,8);
   end
   
   return threat;
end

--[[
   Debug function that writes card data
  ]]
printCardData = function(data,size,turn)
   debugFile = "cards.txt";
   local dFile = io.open(debugFile,"a")
   dFile:write("Player "..turn..":\n")
   
   local i = 0;
   while (i < size) do
      i = i + 1
      if(data[i] ~= nil) then
         dFile:write(data[i]..",")
      else
         dFile:write("NIL,")
      end
   end
   dFile:write("\n")
   dFile:close()
end
