1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331
|
--[[
Hearts - Luis.lua
Copyright 2006 Sander Marechal
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
]]
--[[
This player is based on the logic of KDE Hearts' computerplayer3_2.cpp.
It is named Luis after it's original creator, Luis Pedro Coelho
]]
-- initialize some variables
math.randomseed = os.time()
queen_played = false
hearts_played = false
trick_starts = {}
trick_starts[clubs] = 0
trick_starts[diamonds] = 0
trick_starts[spades] = 0
trick_starts[hearts] = 0
-- turn debugging strings on or off
---[[
function print(...)
return
end
--]]
-- remove card from hand and add it to result
function select_card(card, result, hand)
local i = have_card(card, hand)
if i then
table.insert(result, card)
table.remove(hand, i)
end
end
-- select the cards to be passed off
function select_cards()
local result = {}
local index = 0
-- first pass on the high spades
if have_card({spades, ace_high}, hand) then select_card({spades, ace_high}, result, hand) end
if have_card({spades, king}, hand) then select_card({spades, king}, result, hand) end
if have_card({spades, queen}, hand) then select_card({spades, queen}, result, hand) end
-- pass off other cards
while table.getn(result) < 3 do
local clubs_list = get_cards_of_suit(hand, clubs)
local diamonds_list = get_cards_of_suit(hand, diamonds)
local spades_list = get_cards_of_suit(hand, spades)
local hearts_list = get_cards_of_suit(hand, hearts)
-- if we can clear a suit with one card then do so, else pick a hearts or a high card
if table.getn(clubs_list) == 1 then select_card(clubs_list[1], result, hand)
elseif table.getn(diamonds_list) == 1 then select_card(diamonds_list[1], result, hand)
elseif table.getn(hearts_list) > 0 then
table.sort(hearts_list, descending)
select_card(hearts_list[1], result, hand)
else
table.sort(hand, descending)
select_card(hand[1], result, hand)
end
end
return result
end
-- open a trick
function open_trick()
-- If I have only hearts, return the lowest
if table.getn(hand) == table.getn(get_cards_of_suit(hand, hearts)) then
print("Forced hearts opening")
return hand[1]
end
-- If I have the two of clubs then play it
-- FIXME: This should also honor the clubs_lead = false rule
if have_card({clubs, two}, hand) then
print("playing two of clubs")
return {clubs, two}
end
-- If I don't have the queen of spades or higher and the queen hasn't been played, open with high spades
if not have_card({spades, queen}, hand) and not have_card({spades, king}, hand) and not have_card({spades, ace_high}, hand) and not queen_played and have_suit(spades, hand) then
local spades_list = get_cards_of_suit(hand, spades)
table.sort(spades_list, descending)
print("try a spades - I have no highs")
return spades_list[1]
end
-- try playing a suit that hasn't been played much
if have_suit(diamonds, hand) and trick_starts[diamonds] < 2 then
local diamonds_list = get_cards_of_suit(hand, diamonds)
table.sort(diamonds_list, descending)
print("try diamonds - not played much")
return diamonds_list[1]
end
if have_suit(clubs, hand) and trick_starts[clubs] == 1 then
local clubs_list = get_cards_of_suit(hand, clubs)
table.sort(clubs_list, descending)
print("try clubs - not played much")
return clubs_list[1]
end
-- a low hearts to give them trouble then
-- FIXME: this should honor the hearts_broken = false rule
if have_suit(hearts, hand) and hearts_played then
local max_hearts = two
local hearts_list = get_valid_cards_of_suit(hand, hearts)
table.sort(hearts_list, ascending)
max_hearts = max_hearts + ((trick_starts[hearts] + 1) * 3)
if hearts_list[1][rank] <= max_hearts then
print("screw 'em with low hearts")
return hearts_list[1]
end
end
-- FIXME: this should honor the hearts_broken = false rule
local suits_to_try = {}
local valid_cards = get_valid_cards()
if have_suit(diamonds, valid_cards) and trick_starts[diamonds] < 4 then table.insert(suits_to_try, diamonds) end
if have_suit(clubs, valid_cards) and trick_starts[clubs] < 4 then table.insert(suits_to_try, clubs) end
if hearts_played and have_suit(hearts, valid_cards) and trick_starts[hearts] < 4 then table.insert(suits_to_try, hearts) end
if queen_played and have_suit(spades, valid_cards) and trick_starts[spades] < 4 then table.insert(suits_to_try, spades) end
table.sort(suits_to_try, random)
local best_score, best_card = 0, nil
for _, suit_to_try in ipairs(suits_to_try) do
local suit_list = get_valid_cards_of_suit(hand, suit_to_try)
table.sort(suit_list, ascending)
local lowest = suit_list[1]
local max_rank = two + ((trick_starts[suit_to_try] + 1) * 3)
local score = max_rank - lowest[rank]
if score > best_score then
best_score = score
best_card = lowest
end
end
if best_card ~= nil then print("a score-based low card") return best_card end
-- I give up. Open with a random card
local valid_cards = get_valid_cards(hand)
print("a random card")
return valid_cards[math.random(table.getn(valid_cards))]
end
-- play a card following the suit
function follow_suit()
-- what to do if we play spades
if trick_get_trump() == spades then
local spades_list = get_cards_of_suit(hand, spades)
if have_card({spades, queen}, hand) then
if have_card({spades, king}, trick) or have_card({spades, ace_high}, trick) then
return {spades, queen}
end
table.sort(spades_list, ascending)
if spades_list[1][rank] == queen then
if table.getn(spades_list) > 1 then
-- I have a high spades to play
print("play high spades while I have queen")
return spades_list[table.getn(spades_list)]
end
-- I'm forced to play the queen :-(
print("forced queen play")
return spades_list[1]
end
-- return the highest card below the queen
for i, card in ipairs(spades_list) do
if spades_list[i+1][rank] == queen then print("high card below queen") return card end
end
end
if have_card({spades, king}, hand) or have_card({spades, ace_high}, hand) then -- I don't have the queen
if trick_get_num_played() == 3 and not have_card({spades, queen}, trick) then
-- get rid of a high spades
table.sort(spades_list, descending)
print("dump high spades")
return spades_list[1]
end
table.sort(spades_list, ascending)
if spades_list[1][rank] >= king then
-- all I have is high spades
print("forced dump of high spades")
return spades_list[table.getn(spades_list)]
end
-- return the highest card below the king
for i, card in ipairs(spades_list) do
if spades_list[i+1][rank] >= king then print("dodge high spades") return card end
end
end
table.sort(spades_list, descending)
print("play a spades")
return spades_list[1]
end
-- what to do if we play hearts
if trick_get_trump() == hearts then
local hearts_list = get_cards_of_suit(hand, hearts)
table.sort(hearts_list, ascending)
highest = get_highest_card()
print("hearts:", have_suit(hearts, hand), "hearts length:", table.getn(get_valid_cards_of_suit(hand, hearts)))
if hearts_list[1][rank] > highest[rank] then
if trick_get_num_played() == 3 then
-- we're going to get it anyway so play the highest
table.sort(hearts_list, descending)
print("force take hearts")
return hearts_list[1]
end
if trick_get_num_played == 2 and hearts_list[1][rank] > highest[rank] + 3 then
-- we're probably going to get it anyway so play the highest
table.sort(hearts_list, descending)
print("probabely force take hearts")
return hearts_list[1]
end
table.sort(hearts_list, ascending)
print("play lowest high hearts")
return hearts_list[1]
end
-- return the highest hearts that is below the highest on the table
table.sort(hearts_list, descending)
for _, card in ipairs(hearts_list) do
if card[rank] < highest[rank] then print("dodge hearts") return card end
end
end
suit_list = get_cards_of_suit(hand, trick_get_trump())
-- dump high cards if we can and we don't take the queen of spades
if trick_starts[trick_get_trump()] < 2 and not have_card({spades, queen}, trick) then
table.sort(suit_list, descending)
print("dump high card")
return suit_list[1]
end
-- If we're the last in this trick, take with high cards
if trick_get_num_played() == 3 and not have_card({spades, queen}, trick) then
table.sort(suit_list, descending)
print("take with high card")
return suit_list[1]
end
-- play the highest card that doesn't win the trick
table.sort(suit_list, ascending)
highest = get_highest_card()
best_card = suit_list[1]
for _, card in ipairs(suit_list) do
if card[rank] > best_card[rank] and card[rank] < highest[rank] then
best_card = card
end
end
print("best card that doesn't win")
return best_card
end
-- play a card when we can't follow the suit
function dont_follow_suit()
local valid_cards = get_valid_cards(hand)
if have_card({spades, queen}, valid_cards) then return {spades, queen} end
if have_card({spades, ace_high}, valid_cards) then return {spades, ace_high} end
if have_card({spades, king}, valid_cards) then return {spades, king} end
table.sort(valid_cards, descending)
local highest = valid_cards[1]
if highest[rank] <= 8 and have_suit(hearts, valid_cards) then
local hearts_list = get_cards_of_suit(valid_cards, hearts)
table.sort(hearts_list, descending)
print("returning highest hearts")
return hearts_list[1]
end
print("returning highest card")
return highest
end
-- play a card
function play_card()
result = {}
if trick_get_num_played() == 0 then
print("opening trick")
result = open_trick()
elseif have_suit(trick_get_trump(), hand) then
print("following suit")
result = follow_suit()
else
print("not following suit")
result = dont_follow_suit()
end
print("playing card:", result[suit], result[rank])
return result
end
-- do some trick post-processing
function trick_end()
-- remember the trump counts
trick_starts[trick_get_trump()] = trick_starts[trick_get_trump()] + 1
-- has hearts been broken?
if table.getn(get_cards_of_suit(trick, hearts)) > 0 then
hearts_played = true
end
end
-- reset some variables so we can start the next round
function round_end()
queen_played = false
hearts_played = false
trick_starts[clubs] = 0
trick_starts[diamonds] = 0
trick_starts[spades] = 0
trick_starts[hearts] = 0
end
|