File: Luis.lua

package info (click to toggle)
gnome-hearts 0.1.3-2etch1
  • links: PTS
  • area: main
  • in suites: etch
  • size: 2,040 kB
  • ctags: 286
  • sloc: sh: 3,514; ansic: 2,511; xml: 2,030; makefile: 100
file content (331 lines) | stat: -rw-r--r-- 11,053 bytes parent folder | download | duplicates (2)
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