Advertisement
Guest User

FE auction system #3

a guest
Sep 3rd, 2017
54
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 25.02 KB | None | 0 0
  1. local auctionStateObj = {}
  2. function auctionStateObj:new()
  3.     local o = {}
  4.     setmetatable(o, self)
  5.     self.__index = self
  6.    
  7.     o.numOf_players = 0
  8.     o.players = {} -- strings
  9.     o.numOf_units = 0
  10.     o.units = {} -- strings
  11.     o.chapters = {} -- chapter the unit appears
  12.     o.promoItems = {} -- item unit uses to promote, if any
  13.     o.promoItemTotals = {}
  14.    
  15.     o.bids = {} -- PxU array of numbers
  16.     o.assignedTo = {} -- PxU array of bools.
  17.     -- units can temporarily be assigned to more than one player if ties exist
  18.     o.wasAssignedTo = {} -- PxU array of bools.
  19.     -- don't reassign to past assignment, avoid cycles with ties
  20.     o.prefViolationFactor = 0 -- compute in initialize, for averagePreferenceViolation()
  21.    
  22.     return o
  23. end
  24.  
  25. -- promo items
  26. local promo_NO = 0 -- can't promote
  27. local promo_HC = 1 -- hero crest
  28. local promo_KC = 2 -- knight crest
  29. local promo_OB = 3 -- orion's bolt
  30. local promo_EW = 4 -- elysian whip
  31. local promo_GR = 5 -- guiding ring
  32. local promo_FC = 6 -- fell contract
  33. local promo_OS = 7 -- ocean seal
  34. local promo_HS = 8 -- heaven seal
  35.  
  36. local promoStrings = {"hC", "kC", "oB", "eW", "gR", "fC", "oS", "hS"}
  37. promoStrings[0] = "No"
  38.  
  39. function auctionStateObj:initialize()
  40.     local totalBids = 0
  41.     for player_i = 1, self.numOf_players do
  42.         self.assignedTo[player_i] = {}
  43.         self.wasAssignedTo[player_i] = {}
  44.         for unit_i = 1, self.numOf_units do
  45.             totalBids = totalBids + self.bids[player_i][unit_i]
  46.             self.assignedTo[player_i][unit_i] = false
  47.             self.wasAssignedTo[player_i][unit_i] = false
  48.         end
  49.     end
  50.    
  51.     for promoItem_i = 0, 8 do
  52.         self.promoItemTotals[promoItem_i] = 0
  53.     end
  54.     for unit_i = 1, self.numOf_units do
  55.         self.promoItemTotals[self.promoItems[unit_i]] =
  56.             self.promoItemTotals[self.promoItems[unit_i]] + 1
  57.     end
  58.    
  59.     self.prefViolationFactor = self.numOf_players/totalBids
  60. end
  61.  
  62. -- can make unbalanced teams
  63. -- assign each unit to the player(s) with the greatest bid for them
  64. function auctionStateObj:initialAssign()   
  65.     for unit_i = 1, self.numOf_units do
  66.         local maxBid = -1
  67.         for player_i = 1, self.numOf_players do
  68.             if self.bids[player_i][unit_i] >= maxBid then
  69.                 maxBid = self.bids[player_i][unit_i]
  70.             end
  71.         end
  72.        
  73.         for player_i = 1, self.numOf_players do
  74.             self.assignedTo[player_i][unit_i] = (self.bids[player_i][unit_i] == maxBid)
  75.         end
  76.     end
  77. end
  78.  
  79. function auctionStateObj:printBids()
  80.     print("")
  81.     print("-BIDS-")
  82.     local str = "           "
  83.     for player_i = 1, self.numOf_players do
  84.         str = str .. string.format("%-10.10s ", self.players[player_i])
  85.     end
  86.     print(str)
  87.    
  88.     for unit_i = 1, self.numOf_units do
  89.         str = string.format("%-10.10s ", self.units[unit_i])
  90.         for player_i = 1, self.numOf_players do
  91.             str = str ..  string.format("%-10.10s ",
  92.                 string.format("%05.2f", self.bids[player_i][unit_i]))
  93.         end
  94.        
  95.         print(str)
  96.     end
  97. end
  98.  
  99. function auctionStateObj:findOwner(unit_i)
  100.     for player_i = 1, self.numOf_players do
  101.         if self.assignedTo[player_i][unit_i] then
  102.             return player_i
  103.         end
  104.     end
  105.     return 0
  106. end
  107.  
  108. -- this function ensures that the expected value of over or underbidding
  109. -- is strictly non-positive (i.e. strategically dominated by true bidding)
  110. function auctionStateObj:handicapPrice(unit_i)
  111.     local ownerBid = self.bids[self:findOwner(unit_i)][unit_i]
  112.    
  113.     -- find largest bid not greater than that, from player not assigned to, at least 0
  114.     -- bids from past owners should be larger than current, hence not considered again
  115.     local secondPrice = -1
  116.     for player_i = 1, self.numOf_players do
  117.         if (self.bids[player_i][unit_i] <= ownerBid and
  118.            self.bids[player_i][unit_i] >= secondPrice and
  119.            not self.assignedTo[player_i][unit_i]) then
  120.        
  121.             secondPrice = self.bids[player_i][unit_i]
  122.         end
  123.     end
  124.    
  125.     if secondPrice >= 0 then
  126.         return secondPrice + (ownerBid - secondPrice)/self.numOf_players
  127.     else -- owner(s) in last place, special behavior, pay 2nd-to-last price
  128.    
  129.         local secondLastPrice = 999
  130.         for player_i = 1, self.numOf_players do
  131.             if (self.bids[player_i][unit_i] <= secondLastPrice and
  132.                not self.assignedTo[player_i][unit_i]) then
  133.                
  134.                 secondLastPrice = self.bids[player_i][unit_i]
  135.             end
  136.         end
  137.         if secondLastPrice == 999 then
  138.             return self.bids[1][unit_i] -- assigned to all players
  139.         end
  140.        
  141.         return ownerBid + (secondLastPrice - ownerBid)/self.numOf_players
  142.     end
  143. end
  144.  
  145. function auctionStateObj:totalHandicap(player_i)
  146.     local hc = 0
  147.     for unit_i = 1, self.numOf_units do
  148.         if self.assignedTo[player_i][unit_i] then
  149.             hc = hc + self:handicapPrice(unit_i)
  150.         end
  151.     end
  152.     return hc
  153. end
  154.  
  155. -- determines tiebreaks, give to team with lowest total value
  156. function auctionStateObj:totalValue(player_i)
  157.     local value = 0
  158.     for unit_i = 1, self.numOf_units do
  159.         if self.assignedTo[player_i][unit_i] then
  160.             value = value + self.bids[player_i][unit_i]
  161.         end
  162.     end
  163.     return value
  164. end
  165.  
  166. function auctionStateObj:printTeams()
  167.     local smallestHCtotal = 999
  168.     for player_i = 1, self.numOf_players do
  169.         if self:totalHandicap(player_i) < smallestHCtotal then
  170.             smallestHCtotal = self:totalHandicap(player_i)
  171.         end
  172.     end
  173.    
  174.     for player_i = 1, self.numOf_players do
  175.         print("")
  176.         print(string.format("%-10.10s price | bid", self.players[player_i]))
  177.        
  178.         for unit_i = 1, self.numOf_units do
  179.             if self.assignedTo[player_i][unit_i] then
  180.                 local str = string.format("%-10.10s %05.2f | %05.2f",
  181.                     self.units[unit_i], self:handicapPrice(unit_i), self.bids[player_i][unit_i])           
  182.                 print(str)
  183.             end
  184.         end
  185.         print(string.format("TOTAL      %05.2f, relative hc %05.2f",
  186.             self:totalHandicap(player_i), self:totalHandicap(player_i)-smallestHCtotal))       
  187.     end
  188. end
  189.  
  190. -- actually should be "multipleAssignmentExistsFor"
  191. function auctionStateObj:tieExistsFor(unit_i)
  192.     -- find unit assigned to multiple teams
  193.     local alreadyAssigned = false
  194.     for player_i = 1, self.numOf_players do
  195.         if self.assignedTo[player_i][unit_i] then
  196.             if alreadyAssigned then
  197.                 return true
  198.             else
  199.                 alreadyAssigned = true
  200.             end
  201.         end
  202.     end
  203.     return false
  204. end
  205.  
  206. -- returns earliest index of a tie if true, false if not
  207. function auctionStateObj:tieExists()
  208.     for unit_i = 1, self.numOf_units do
  209.         if self:tieExistsFor(unit_i) then
  210.             return unit_i
  211.         end
  212.     end
  213.     return false
  214. end
  215.  
  216. -- break ties in order of list (recruitment?)
  217. -- unit goes to team with lower total valuation, or reverse player list order
  218. -- DO NOT add to hasBeenAssignedTo, don't want underfilled teams losing access by ties with
  219. -- filled/overfilled teams, thereby "leaking" units that become impossible to assign
  220. function auctionStateObj:resolveTie(printV)
  221.     local str = "Tiebreak:     "
  222.     local tie_i = self:tieExists()
  223.    
  224.     if tie_i then
  225.         -- who among tied players has lowest total value?
  226.         local lowestValue = 999
  227.         local lowestValue_i = 0
  228.         for player_i = 1, self.numOf_players do
  229.             if self.assignedTo[player_i][tie_i] and
  230.                 self:totalValue(player_i) <= lowestValue then
  231.                
  232.                 lowestValue = self:totalValue(player_i)
  233.                 lowestValue_i = player_i
  234.             end
  235.         end
  236.        
  237.         -- unassign unit from every other player, assign to player
  238.         for player_i = 1, self.numOf_players do
  239.             if self.assignedTo[player_i][tie_i] then
  240.                 if player_i ~= lowestValue_i then
  241.                     str = str .. string.format("%-10.10s ", self.players[player_i])
  242.                 end
  243.             end
  244.             self.assignedTo[player_i][tie_i] = (player_i == lowestValue_i)
  245.         end
  246.         str = str .. "->" .. string.format("%-10.10s ", self.units[tie_i])
  247.        
  248.         -- print to player
  249.         for player_i = 1, self.numOf_players do
  250.             if self.assignedTo[player_i][tie_i] then
  251.                 str = str .. "->" .. string.format("%-10.10s ", self.players[player_i])
  252.             end
  253.         end
  254.     end
  255.     if printV then
  256.         print(str)
  257.     end
  258. end
  259.  
  260. function auctionStateObj:teamSize(player_i)
  261.     local ret = 0
  262.     for unit_i = 1, self.numOf_units do
  263.         if self.assignedTo[player_i][unit_i] then
  264.             ret = ret + 1
  265.         end
  266.     end
  267.     return ret
  268. end
  269.  
  270. function auctionStateObj:filledNum()
  271.     return self.numOf_units/self.numOf_players
  272. end
  273.  
  274. function auctionStateObj:teamOverfilled(player_i)
  275.     return self:teamSize(player_i) >= self:filledNum() + 1
  276. end
  277.  
  278. function auctionStateObj:existOverfilledTeam()
  279.     for player_i = 1, self.numOf_players do
  280.         if self:teamOverfilled(player_i) then
  281.             return true
  282.         end
  283.     end
  284.     return false
  285. end
  286.  
  287. function auctionStateObj:teamUnderfilled(player_i)
  288.     return self:teamSize(player_i) <= self:filledNum() - 1
  289. end
  290.  
  291. function auctionStateObj:existUnderfilledTeam()
  292.     for player_i = 1, self.numOf_players do
  293.         if self:teamUnderfilled(player_i) then
  294.             return true
  295.         end
  296.     end
  297.     return false
  298. end
  299.  
  300. function auctionStateObj:reassignFrom(overfilled, printV)  
  301.     local str = "Underfilled:  "
  302.     if overfilled then str = "Overfilled:   " end
  303.  
  304.     -- find least desired unit from overfilled/filled
  305.     local leastDesired_i = 0
  306.     local leastDesired_value = 999
  307.    
  308.     -- find lowest differential from current to next
  309.     -- minimize preference violation
  310.     local lowestDif_i = 0
  311.     local lowestDif = 999
  312.    
  313.     for player_i = 1, self.numOf_players do
  314.         if (overfilled and self:teamOverfilled(player_i)) -- moving from overfilled
  315.             or (not overfilled and not self:teamUnderfilled(player_i))then -- moving from filled
  316.            
  317.             for unit_i = 1, self.numOf_units do
  318.                 if self.assignedTo[player_i][unit_i] then
  319.                     -- found a player/unit combo that can move                 
  320.                     if self.bids[player_i][unit_i] <= leastDesired_value then
  321.                         leastDesired_i = unit_i
  322.                         leastDesired_value = self.bids[player_i][unit_i]
  323.                     end
  324.                    
  325.                     -- check other players for differential
  326.                     for player_j = 1, self.numOf_players do
  327.                         if not self.wasAssignedTo[player_j][unit_i] and
  328.                             not self.assignedTo[player_j][unit_i] then
  329.                            
  330.                             local dif = self.bids[player_i][unit_i] - self.bids[player_j][unit_i]
  331.                            
  332.                             if dif <= lowestDif then
  333.                                 lowestDif = dif
  334.                                 lowestDif_i = unit_i
  335.                             end
  336.                         end
  337.                     end
  338.                 end
  339.             end
  340.         end
  341.     end
  342.        
  343.     local reassign_i = lowestDif_i
  344.    
  345.     -- remove from teams that have, and mark as previously had
  346.     for player_i = 1, self.numOf_players do
  347.         if self.assignedTo[player_i][reassign_i] then
  348.             self.assignedTo[player_i][reassign_i] = false
  349.             self.wasAssignedTo[player_i][reassign_i] = true
  350.            
  351.             str = str .. string.format("%-10.10s ->%-10.10s ->",
  352.                 self.players[player_i], self.units[reassign_i])
  353.         end
  354.     end
  355.    
  356.     -- reassign to team that desires most, that hasn't had
  357.     local mostDesire = -99
  358.     local mostDesire_players = {}
  359.     for player_i = 1, self.numOf_players do
  360.         if self.bids[player_i][reassign_i] >= mostDesire and
  361.             not self.wasAssignedTo[player_i][reassign_i] then
  362.            
  363.             mostDesire = self.bids[player_i][reassign_i]   
  364.         end
  365.     end
  366.    
  367.     for player_i = 1, self.numOf_players do
  368.         if self.bids[player_i][reassign_i] >= mostDesire and
  369.             not self.wasAssignedTo[player_i][reassign_i] then
  370.            
  371.             self.assignedTo[player_i][reassign_i] = true
  372.            
  373.             str = str .. string.format("%-10.10s ",self.players[player_i])
  374.         end
  375.     end
  376.     str = str .. string.format("%04.2f pref violation", lowestDif)
  377.    
  378.     if printV then
  379.         print(str)
  380.     end
  381. end
  382. -- can leave suboptimal preference violation in the following way:
  383. -- unit A drops from overfilled player 1 to player 2
  384. -- player 2 is now overfilled and drops A to player 3
  385. -- player 3 is now overfilled and drops unit B to player 2
  386. -- player 2 is overfilled again and drops unit C to player 4
  387. -- now no one is overfilled: 1-D, 2-B, 3-A, 4-C
  388. -- however, it might be that 2 bid more on A than 3 did, and 3 bid more on B than 2 did
  389. -- so swapping would reduce the preference violation
  390.  
  391. function auctionStateObj:swapUnits(unit_i, unit_j, printV)
  392.     local player_i = self:findOwner(unit_i)
  393.     local player_j = self:findOwner(unit_j)
  394.  
  395.     if printV then
  396.         print(string.format("Swapping: %-10.10s %-10.10s <-> %-10.10s %-10.10s",
  397.                 self.players[player_i], self.units[unit_i],
  398.                 self.players[player_j], self.units[unit_j]))
  399.     end
  400.    
  401.     self.assignedTo[player_i][unit_i] = false
  402.     self.assignedTo[player_j][unit_i] = true
  403.    
  404.     self.assignedTo[player_j][unit_j] = false
  405.     self.assignedTo[player_i][unit_j] = true
  406. end
  407.  
  408. -- returns true if a swap was made
  409. -- otherwise returns false
  410. -- makes max swap first so as to not be subject to recruitment order, player order etc
  411. function auctionStateObj:cleanupPrefViolation(printV)  
  412.     local bestSwapValue = 0
  413.     local maxSwap_i = 0
  414.     local maxSwap_j = 0
  415.    
  416.     for unit_i = 1, self.numOf_units do
  417.         local player_i = self:findOwner(unit_i)
  418.         for unit_j = 1, self.numOf_units do
  419.             local player_j = self:findOwner(unit_j)
  420.            
  421.             local swapValue = (self.bids[player_i][unit_j] + self.bids[player_j][unit_i]) -- swapped bid sum
  422.                 - (self.bids[player_i][unit_i] + self.bids[player_j][unit_j]) -- current bid
  423.            
  424.             if bestSwapValue < swapValue then
  425.                 bestSwapValue = swapValue
  426.                 maxSwap_i = unit_i
  427.                 maxSwap_j = unit_j
  428.             end
  429.         end
  430.     end
  431.    
  432.     if maxSwap_i ~= 0 then     
  433.         self:swapUnits(maxSwap_i, maxSwap_j, printV)
  434.         return true
  435.     end
  436.     return false
  437. end
  438.  
  439. -- scale to average bid
  440. -- preference violation is a measure of how much the owner's bid differs from the highest bid.
  441. -- with no other considerations, the unit should go to highest bidder.
  442. -- however, team balance and chapter gap smoothing provide an incentive to violate that principle.
  443. function auctionStateObj:averagePreferenceViolation()
  444.     local violations = 0
  445.     for unit_i = 1, self.numOf_units do
  446.         local highestBid = 0
  447.         for player_i = 1, self.numOf_players do
  448.             if highestBid < self.bids[player_i][unit_i] then
  449.                 highestBid = self.bids[player_i][unit_i]
  450.             end
  451.         end
  452.         violations = violations + highestBid - self.bids[self:findOwner(unit_i)][unit_i]
  453.     end
  454.    
  455.     return violations*self.prefViolationFactor
  456. end
  457.  
  458. -- takes 2D array, eg first dimension players, second dimension value
  459. local function sumOfSquares(array)
  460.     local sum = 0
  461.     local i = 1 -- starting at 1 correctly skips non-promoters
  462.     while array[i] do
  463.         local sumOfSquares = 0
  464.         local j = 1
  465.         while array[i][j] do
  466.             sumOfSquares = sumOfSquares + array[i][j]*array[i][j]
  467.             j = j + 1
  468.             if j > 10000 then print("BAD J") end
  469.         end
  470.         sum = sum + sumOfSquares
  471.         i = i + 1
  472.        
  473.         if i > 10000 then print("BAD I") end
  474.     end
  475.     return sum
  476. end
  477.  
  478. -- gaps between drafted units appearing, numOf_player X (teamSize + 1) array
  479. -- values normalized
  480. function auctionStateObj:chapterGaps(printV)
  481.     local ret = {}
  482.     local totalGap = self.chapters[self.numOf_units] - self.chapters[1]
  483.    
  484.     if printV then
  485.         print()
  486.         print("Chapter gaps")
  487.     end
  488.     for player_i = 1, self.numOf_players do
  489.         if printV then
  490.             print()
  491.             print(self.players[player_i])
  492.         end
  493.        
  494.         ret[player_i] = {}
  495.         local lastChapter = self.chapters[1] -- first chapter a unit can be available
  496.         local gap_i = 1
  497.         local gap = 0
  498.         local normalized = 0
  499.         for unit_i = 1, self.numOf_units do
  500.             if self.assignedTo[player_i][unit_i] then
  501.            
  502.                 gap = self.chapters[unit_i] - lastChapter
  503.                 normalized = gap/totalGap
  504.                
  505.                 if printV then
  506.                     print(string.format("%-10.10s %2d %2d/%2d=%4.2f %4.2f",
  507.                         self.units[unit_i], self.chapters[unit_i], gap, totalGap,
  508.                         normalized, normalized*normalized))
  509.                 end            
  510.                 ret[player_i][gap_i] = normalized
  511.                 lastChapter = self.chapters[unit_i]
  512.                 gap_i = gap_i + 1
  513.             end
  514.         end
  515.        
  516.         -- gap to end
  517.         gap = self.chapters[self.numOf_units] - lastChapter
  518.         normalized = gap/totalGap
  519.        
  520.         if printV then
  521.             print(string.format("-end-      %2d %2d/%2d=%4.2f %4.2f",
  522.                 self.chapters[self.numOf_units], gap, totalGap,
  523.                 normalized, normalized*normalized))
  524.         end
  525.         ret[player_i][gap_i] = normalized
  526.        
  527.         if printV then
  528.             local sumOfSq = 0
  529.             for gap_i2 = 1, gap_i do
  530.                 sumOfSq = sumOfSq + ret[player_i][gap_i2]*ret[player_i][gap_i2]
  531.             end
  532.             print(string.format("sum of squares:          %4.2f", sumOfSq))
  533.         end
  534.     end
  535.    
  536.     if printV then
  537.         print()
  538.         print("total " .. tostring(sumOfSquares(ret)))
  539.     end
  540.    
  541.     return ret
  542. end
  543.  
  544. -- number of each promo type, numOf_player X 8 array
  545. -- values normalized
  546. function auctionStateObj:promoClasses(printV)
  547.     local ret = {} -- normalized values
  548.     local count = {} -- raw counts
  549.    
  550.     if printV then
  551.         print()
  552.         print("Promo classes")
  553.     end
  554.     for player_i = 1, self.numOf_players do
  555.         if printV then
  556.             print()
  557.             print(self.players[player_i])
  558.         end
  559.        
  560.         ret[player_i] = {}
  561.         count[player_i] = {}
  562.         for promoItem_i = 0, 8 do
  563.             count[player_i][promoItem_i] = 0
  564.         end
  565.        
  566.         for unit_i = 1, self.numOf_units do
  567.             if self.assignedTo[player_i][unit_i] then
  568.                 count[player_i][self.promoItems[unit_i]] =
  569.                     count[player_i][self.promoItems[unit_i]] + 1
  570.             end
  571.         end
  572.        
  573.         local sumOfSq = 0
  574.         for promoItem_i = 0, 8 do
  575.             local normalized = count[player_i][promoItem_i]/self.promoItemTotals[promoItem_i]
  576.             ret[player_i][promoItem_i] = normalized
  577.            
  578.             local square = 0
  579.             if promoItem_i > 0 then -- don't count non promotions
  580.                 square = normalized*normalized
  581.             end
  582.             sumOfSq = sumOfSq + square
  583.            
  584.             if printV and count[player_i][promoItem_i] > 0 then
  585.                 print(string.format("%s %d/%d=%4.2f %4.2f",
  586.                     promoStrings[promoItem_i], count[player_i][promoItem_i],
  587.                     self.promoItemTotals[promoItem_i], normalized, square))
  588.             end
  589.         end
  590.         if printV then print(string.format("sum of squares:  %4.2f", sumOfSq)) end
  591.     end
  592.    
  593.     if printV then
  594.         print()
  595.         print(string.format("total %4.2f", sumOfSquares(ret)))
  596.     end
  597.    
  598.     return ret
  599. end
  600.  
  601. -- see how each swap affects preference violation, chapterGap sum of squares, and promoClasses SoS
  602. -- if favorable, swap them and return true, else return false
  603. function auctionStateObj:finesseTeams(threshold, printV)   
  604.     local bestSwapValue = -1
  605.     -- if no PrefVio loss, then just gain, don't divide by 0
  606.     local noPrefVioLoss = false
  607.    
  608.     local maxSwap_i = 0
  609.     local maxSwap_j = 0
  610.    
  611.     local savedPrefViolation = self:averagePreferenceViolation()
  612.     local savedCGSoS = sumOfSquares(self:chapterGaps())
  613.     local savedPCSoS = sumOfSquares(self:promoClasses())
  614.    
  615.     for unit_i = 1, self.numOf_units do
  616.         local player_i = self:findOwner(unit_i)
  617.        
  618.         for unit_j = unit_i+1, self.numOf_units do
  619.             local player_j = self:findOwner(unit_j)
  620.            
  621.             -- try swap
  622.             if player_j ~= player_i then
  623.                 self:swapUnits(unit_i, unit_j)
  624.                
  625.                 local prefVioLoss = self:averagePreferenceViolation() - savedPrefViolation
  626.                
  627.                 -- note that even after cleanup, this function will violate the preferences
  628.                 -- so prefVioLoss will become negative, and the function will try to
  629.                 -- switch back unless we check
  630.                 if prefVioLoss >= 0 then
  631.                     local CGSoSgain = savedCGSoS - sumOfSquares(self:chapterGaps())
  632.                     local PCSoSgain = savedPCSoS - sumOfSquares(self:promoClasses())
  633.                    
  634.                     local weightedGain = CGSoSgain + PCSoSgain
  635.                    
  636.                     local swapValue = -1
  637.                     if noPrefVioLoss then
  638.                         if prefVioLoss == 0 then
  639.                             swapValue = weightedGain
  640.                         end
  641.                     else
  642.                         if prefVioLoss == 0 and weightedGain > 0 then
  643.                             noPrefVioLoss = true                       
  644.                             bestSwapValue = -1 -- reset standards                          
  645.                             swapValue = weightedGain
  646.                         else
  647.                             swapValue = weightedGain/prefVioLoss
  648.                         end
  649.                     end
  650.                    
  651.                     if bestSwapValue < swapValue then
  652.                         bestSwapValue = swapValue
  653.                         maxSwap_i = unit_i
  654.                         maxSwap_j = unit_j
  655.                     end
  656.                 end
  657.                
  658.                 self:swapUnits(unit_i, unit_j)
  659.             end
  660.         end
  661.     end
  662.    
  663.     local function printChange()
  664.         print(string.format("old CGSoS %8.6f, old PCSoS %8.6f, old prefVio %8.6f",
  665.             savedCGSoS, savedPCSoS, savedPrefViolation*100) .. "%")
  666.        
  667.         self:swapUnits(maxSwap_i, maxSwap_j, true)
  668.        
  669.         print(string.format("new CGSoS %8.6f, new PCSoS %8.6f, new prefVio %8.6f",
  670.             sumOfSquares(self:chapterGaps()), sumOfSquares(self:promoClasses()),
  671.             self:averagePreferenceViolation()*100) .. "%")
  672.            
  673.         print(string.format("value %f", bestSwapValue))
  674.     end
  675.    
  676.     if (threshold <= bestSwapValue) or noPrefVioLoss then
  677.         if printV then
  678.             print()
  679.             printChange() -- includes swap
  680.         else
  681.             self:swapUnits(maxSwap_i, maxSwap_j, false)
  682.         end
  683.         return true
  684.     end
  685.  
  686.     if printV then
  687.         print()
  688.         print("no acceptable swap, best candidate")
  689.         printChange()
  690.        
  691.         self:swapUnits(maxSwap_i, maxSwap_j, false) -- swap back, printChange makes swap
  692.     end
  693.     return false
  694. end
  695.  
  696. function auctionStateObj:standardProcess()     
  697.     self:initialize()
  698.     self:printBids()
  699.     self:initialAssign()
  700.    
  701.     print()
  702.     print("Unbalanced teams")
  703.    
  704.     self:printTeams()
  705.  
  706.     print()
  707.     print("Balancing")
  708.    
  709.     while (self:existUnderfilledTeam()
  710.         or self:existOverfilledTeam()
  711.         or self:tieExists()) do
  712.        
  713.         while (self:existOverfilledTeam() or self:tieExists()) do
  714.             while self:tieExists() do
  715.                 self:resolveTie(true)
  716.             end
  717.            
  718.             if self:existOverfilledTeam() then
  719.                 self:reassignFrom(true, true)
  720.             end
  721.         end
  722.        
  723.         if self:existUnderfilledTeam() then
  724.             self:reassignFrom(false, true)
  725.         end
  726.     end
  727.  
  728.     self:printTeams()
  729.    
  730.     print()
  731.     print(string.format("average preference violation: %8.6f",
  732.         self:averagePreferenceViolation()*100) .. "%")
  733.    
  734.     print("cleaning up preference violations") 
  735.     while(self:cleanupPrefViolation(true)) do end
  736.    
  737.     print(string.format("average preference violation: %8.6f",
  738.         self:averagePreferenceViolation()*100) .. "%")
  739.    
  740.     self:chapterGaps(true)
  741.     self:promoClasses(true)
  742.    
  743.     print()
  744.     print("Attempting to balance chapter gaps and promotion classes")  
  745.     while self:finesseTeams(40, true) do
  746.         emu.frameadvance() -- prevent unresponsiveness
  747.     end
  748.    
  749.     print()
  750.     print("--Final teams--")
  751.     self:printTeams()
  752.    
  753.     print()
  754.     print("END")
  755. end
  756.  
  757. --"Franz", "Gilliam", "Moulder", "Vanessa", "Ross", "Garcia","Neimi", "Colm", "Artur", "Lute", "Natasha", "Joshua", "Forde", "Kyle", "Tana", "Amelia", "Innes", "Gerik", "Marisa", "L'Arachel", "Dozla", "Cormag", "Saleh", "Ewan", "Rennac", "Duessel", "Knoll", "Syrene"
  758.  
  759. -- no Wallace/Geitz or Harken/Karel
  760. local FE7auction2 = auctionStateObj:new()
  761. FE7auction2.numOf_players = 5
  762. FE7auction2.players = {"P1", "P2", "P3", "P4", "P5"}
  763. FE7auction2.numOf_units = 35
  764. FE7auction2.units = {
  765. "Matthew", "Serra", "Oswin", "Eliwood", "Lowen", "Rebecca", "Dorcas", "Bartre&Karla",
  766. "Marcus<=19x", "Guy", "Erk", "Priscilla", "Florina", "Lyn", "Sain", "Kent", "Wil", "Raven",
  767. "Lucius", "Canas", "Dart", "Fiora", "Legault", "Marcus>=20", "Isadora", "Heath", "Rath",
  768. "Hawkeye", "Farina", "Pent", "Louise", "Nino", "Jaffar", "Vaida", "Renault"}
  769.  
  770. -- indexed from 0, not 11, gaidens count as normal
  771. FE7auction2.chapters = { -- Matthew is free for ch 11, Lyn~Wil for ch 16
  772. 1, 1, 1, 1, 1, 1, 1, 1,
  773. 1, 2, 4, 4, 6, 7, 7, 7, 7, 7,
  774. 7, 8, 10, 10, 12, 12, 14, 14, 14,
  775. 15, 18, 19, 19, 21, 22, 23, 27
  776. }
  777.  
  778. FE7auction2.promoItems = {
  779. promo_FC, promo_GR, promo_KC, promo_HS, promo_KC, promo_OB, promo_HC, promo_HC,
  780. promo_NO, promo_HC, promo_GR, promo_GR, promo_EW, promo_HS, promo_KC, promo_KC, promo_OB, promo_HC,
  781. promo_GR, promo_GR, promo_OS, promo_EW, promo_FC, promo_NO, promo_NO, promo_EW, promo_OB,
  782. promo_NO, promo_EW, promo_NO, promo_NO, promo_GR, promo_NO, promo_NO, promo_NO
  783. }
  784.  
  785. FE7auction2.bids = {
  786. {5.07, 3.65, 4.28, 6.32, 9.88, 3.02, 3.95, 3.02,
  787. 7.0, 1.2, 9.07, 6.07, 11.38, 1.58, 8.3, 8.2, 2.27, 2.2,
  788. 6.82, 8.32, 2.9, 7.78, 1.18, 7.0, 2.88, 11.38, 5.7,
  789. 2.45, 0.82, 7, 1.13, 4.42, 1.2, 2.95, 0},
  790. {},
  791. {},
  792. {},
  793. {}
  794. }
  795.  
  796. for player_i = 2, 5 do
  797.     local playerWeight = (1 + 0.2*(math.random()-0.5)) -- simulate players bidding higher/lower overall
  798.     for unit_i = 1, FE7auction2.numOf_units do
  799.         FE7auction2.bids[player_i][unit_i] =
  800.             FE7auction2.bids[1][unit_i]
  801.                 * playerWeight * (1 + 0.6*(math.random()-0.5))
  802.     end
  803. end
  804.  
  805. print("FE7auction2")
  806. FE7auction2:standardProcess()
  807.  
  808. local asObj_FE7fourPlayer = auctionStateObj:new()
  809. asObj_FE7fourPlayer.numOf_players = 4
  810. asObj_FE7fourPlayer.players = {"Wargrave", "Carmine", "Horace", "Baldrick"}
  811. asObj_FE7fourPlayer.numOf_units = 32
  812. asObj_FE7fourPlayer.units = {
  813. "Matthew", "Serra", "Oswin", "Eliwood", "Lowen", "Rebecca", "Dorcas", "Bartre+K", "Guy", "Erk",
  814. "Priscilla", "Florina", "Lyn", "Sain", "Kent", "Wil", "Raven", "Lucius", "Canas", "Dart", "Fiora",
  815. "Legault", "Isadora", "Heath", "Rath", "Hawkeye", "Farina", "Pent", "Louise", "Nino", "Jaffar",
  816. "Vaida"}
  817.  
  818. -- indexed from 0, not 11, gaidens count as normal
  819. asObj_FE7fourPlayer.chapters = { -- Matthew is free for ch 11, Lyn~Wil for ch 16
  820. 1, 1, 1, 1, 1, 1, 1, 1, 2, 4,
  821. 4, 6, 7, 7, 7, 7, 7, 7, 8, 10, 10,
  822. 12, 14, 14, 14, 15, 18, 19, 19, 21, 22,
  823. 23
  824. }
  825.  
  826. asObj_FE7fourPlayer.promoItems = {
  827. promo_FC, promo_GR, promo_KC, promo_HS, promo_KC, promo_OB, promo_HC, promo_HC, promo_HC, promo_GR,
  828. promo_GR, promo_EW, promo_HS, promo_KC, promo_KC, promo_OB, promo_HC, promo_GR, promo_GR, promo_OS, promo_EW,
  829. promo_FC, promo_NO, promo_EW, promo_OB, promo_NO, promo_EW, promo_NO, promo_NO, promo_GR, promo_NO,
  830. promo_NO
  831. }
  832.  
  833. asObj_FE7fourPlayer.bids = {
  834. {3.2, 3.5, 3.5, 3.5, 9.8, 1.7, 2.3, 1.9, 1.8, 5.5, 4.5, 12.5, 1.4, 9.2, 8.8, 1.6, 2.5, 3.3, 2.7, 2.2, 6.5, 1.7, 2, 5.2, 2.8, 1.8, 3.1, 3.1, 1.5, 0.9, 1.5, 2.6},
  835. {3, 2, 4, 4.5, 12, 0.5, 6.5, 3, 1, 12, 5, 13, 0.5, 8, 8, 0.5, 2, 9, 12, 1, 7, 1, 5.5, 10.5, 7.5, 3.5, 4, 7, 1, 0, 2.25, 4},
  836. {4.1, 4.1, 5.1, 5.1, 10.1, 3.1, 3.1, 3.1, 0, 8.1, 5.1, 0, 2.1, 6.1, 6.1, 3.1, 2.1, 6.1, 7.1, 2.1, 10.1, 0, 1.1, 10.1, 5.1, 2.1, 0.1, 2.1, 0.1, 0.2, 1.1, 1.1},
  837. {8, 5, 6, 10, 12, 3, 1, 1, 1, 8, 9, 11, 4, 10, 10, 2, 1, 4, 4, 5, 7, 2, 6, 14, 4, 4, 3, 7, 2, 15, 2, 2}}
  838.  
  839. --print("FE7auction1")
  840. --asObj_FE7fourPlayer:standardProcess()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement