Merge lp:~rschroll/euchre/refactor into lp:euchre

Proposed by Robert Schroll
Status: Needs review
Proposed branch: lp:~rschroll/euchre/refactor
Merge into: lp:euchre
Diff against target: 1073 lines (+467/-433)
3 files modified
ai_basic.js (+337/-0)
main.qml (+30/-433)
util.js (+100/-0)
To merge this branch: bzr merge lp:~rschroll/euchre/refactor
Reviewer Review Type Date Requested Status
Robert Schroll (community) Needs Fixing
Euchre development team Pending
Review via email: mp+252521@code.launchpad.net

Description of the change

I'm interested in trying to improve the AI a little bit. But I wanted to clean up a few bits of the code before attacking that.

The first commit moves all of the card images into a separate directory. This cleans up the directory view in Qt Creator, which will be nice if we start having more source files.

The second commit moves the AI code into a separate library. Each of the AIs is now encapsulated as an object, which should prove helpful if and when we try to keep track of what each player should know. As part of this, some utility code gets split off into its own library, so both the QML file and the AI library can use it. Since everyone needs to know what trump is, that's saved in the utility library. This may not be the best idea; perhaps there should be a game state object that contains this and other information.

To post a comment you must log in.
Revision history for this message
Robert Schroll (rschroll) wrote :

This is broken when everyone passes twice. Please ignore this for now while I track that down.

review: Needs Fixing
lp:~rschroll/euchre/refactor updated
93. By Robert Schroll

Clear hands before redealing

Usually the hands are empty as a result of play, but this isn't the case
if no one called on the previous deal.

Revision history for this message
Robert Ancell (robert-ancell) wrote :

Any progress on this? It might be easier to split into a couple of MPs.

Unmerged revisions

93. By Robert Schroll

Clear hands before redealing

Usually the hands are empty as a result of play, but this isn't the case
if no one called on the previous deal.

92. By Robert Schroll

Split AI logic into separate library

This should help us in improving the AI. This requires some utility
functions used by the AIs and the main logic to be split into a utility
library. We also store the trump suit there, which may or may not be a
good idea.

91. By Robert Schroll

Move card images into separate directory

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'ai_basic.js'
2--- ai_basic.js 1970-01-01 00:00:00 +0000
3+++ ai_basic.js 2015-03-11 05:38:33 +0000
4@@ -0,0 +1,337 @@
5+/*
6+ * Copyright (C) 2013 Robert Ancell <robert.ancell@gmail.com>
7+ *
8+ * This program is free software: you can redistribute it and/or modify it under
9+ * the terms of the GNU General Public License as published by the Free Software
10+ * Foundation, either version 3 of the License, or (at your option) any later
11+ * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
12+ * license.
13+ */
14+
15+.pragma library
16+.import "util.js" as Util
17+
18+function AI (hand) {
19+ this.hand = hand
20+}
21+
22+// Get an estimate on the number of tricks we will win
23+AI.prototype.get_hand_strength = function (trump_suit) {
24+ var hand = this.hand
25+ function have_card (number, suit) {
26+ for (var i = 0; i < hand.length; i++)
27+ if (hand[i].number == number && hand[i].suit == suit)
28+ return true
29+ return false
30+ }
31+ function suit_count (suit) {
32+ var n = 0
33+ for (var i = 0; i < hand.length; i++) {
34+ var s = hand[i].suit
35+ if (hand[i].number == "J" && hand[i].suit == Util.get_off_suit (trump_suit))
36+ s = trump_suit
37+ if (s == suit)
38+ n++
39+ }
40+ return n
41+ }
42+
43+ // FIXME: Should take into account if a card has been turned down (i.e. treat King offsuit as Ace offsuit if Ace was discarded)
44+
45+ // Count how many consecutive top trumps we have
46+ var n_top_trumps = 0
47+ if (have_card ("J", trump_suit)) {
48+ n_top_trumps++
49+ if (have_card ("J", Util.get_off_suit (trump_suit))) {
50+ n_top_trumps++
51+ if (have_card ("A", trump_suit)) {
52+ n_top_trumps++
53+ if (have_card ("K", trump_suit)) {
54+ n_top_trumps++
55+ if (have_card ("Q", trump_suit))
56+ n_top_trumps++
57+ }
58+ }
59+ }
60+ }
61+
62+ // Might get something with other trumps
63+ var n_other_trumps = suit_count (trump_suit) - n_top_trumps
64+
65+ // Count how many Ace off-suits we have
66+ var n_ace_offsuits = 0
67+ if (trump_suit != "♠" && have_card ("A", "♠"))
68+ n_ace_offsuits++
69+ if (trump_suit != "♣" && have_card ("A", "♣"))
70+ n_ace_offsuits++
71+ if (trump_suit != "♥" && have_card ("A", "♥"))
72+ n_ace_offsuits++
73+ if (trump_suit != "♦" && have_card ("A", "♦"))
74+ n_ace_offsuits++
75+
76+ // Kings and queens might give something if there are other cards to help
77+ var n_king_offsuits = 0
78+ if (trump_suit != "♠" && have_card ("K", "♠") && suit_count ("♠") > 1)
79+ n_king_offsuits++
80+ if (trump_suit != "♣" && have_card ("K", "♣") && suit_count ("♣") > 1)
81+ n_king_offsuits++
82+ if (trump_suit != "♥" && have_card ("K", "♥") && suit_count ("♥") > 1)
83+ n_king_offsuits++
84+ if (trump_suit != "♦" && have_card ("K", "♦") && suit_count ("♦") > 1)
85+ n_king_offsuits++
86+ var n_queen_offsuits = 0
87+ if (trump_suit != "♠" && have_card ("Q", "♠") && suit_count ("♠") > 2)
88+ n_queen_offsuits++
89+ if (trump_suit != "♣" && have_card ("Q", "♣") && suit_count ("♣") > 2)
90+ n_queen_offsuits++
91+ if (trump_suit != "♥" && have_card ("Q", "♥") && suit_count ("♥") > 2)
92+ n_queen_offsuits++
93+ if (trump_suit != "♦" && have_card ("Q", "♦") && suit_count ("♦") > 2)
94+ n_queen_offsuits++
95+
96+ // FIXME: Should simulate and find the best values for these weightings
97+ return n_top_trumps + n_other_trumps * 0.5 + n_ace_offsuits + n_king_offsuits * 0.5 + n_queen_offsuits * 0.5
98+}
99+
100+// Return true if the AI will order the dealer
101+// dealer is number of players to dealer from self
102+AI.prototype.will_order = function (dealer, kitty_card) {
103+ // If the dealer is our partner then expect more from then
104+ // FIXME: Should simulate and find the best values for these weightings
105+ var partner_n = 0.5
106+ if (dealer == 2) {
107+ if (kitty_card.number == "J")
108+ partner_n = 1.5
109+ else
110+ partner_n = 0.75
111+ }
112+
113+ // Work out how strong we are with this trump
114+ var n
115+ var swap
116+ var swap_index
117+ if (dealer == 0) {
118+ swap = this.pick_swap (kitty_card)
119+ if (swap != undefined) {
120+ swap_index = this.hand.indexOf (swap)
121+ this.hand[swap_index] = kitty_card
122+ }
123+ }
124+ n = this.get_hand_strength (kitty_card.suit)
125+ if (swap != undefined)
126+ this.hand[swap_index] = swap
127+
128+ // Order if we think we can win this round
129+ return n + partner_n >= 3
130+}
131+
132+// Pick a card to swap when ordered
133+AI.prototype.pick_swap = function (kitty_card) {
134+ var swap_card
135+ var swap_strength = -1
136+ for (var i = 0; i < this.hand.length; i++) {
137+ // Temporarily swap card
138+ var t = this.hand[i]
139+ this.hand[i] = kitty_card
140+
141+ var s = this.get_hand_strength (kitty_card.suit)
142+ if (s > swap_strength) {
143+ swap_card = t
144+ swap_strength = s
145+ }
146+
147+ // Swap card back
148+ this.hand[i] = t
149+ }
150+
151+ return swap_card
152+}
153+
154+// Pick a trump suit to play as or "" to pass
155+AI.prototype.pick_trump = function (player) {
156+ // Try each suit and pick the best one
157+ var trump_suit = ""
158+ var best_n = 0
159+ var n = this.get_hand_strength ("♠")
160+ if (n > best_n) {
161+ trump_suit = "♠"
162+ best_n = n
163+ }
164+ var n = this.get_hand_strength ("♣")
165+ if (n > best_n) {
166+ trump_suit = "♣"
167+ best_n = n
168+ }
169+ var n = this.get_hand_strength ("♥")
170+ if (n > best_n) {
171+ trump_suit = "♥"
172+ best_n = n
173+ }
174+ var n = this.get_hand_strength ("♦")
175+ if (n > best_n) {
176+ trump_suit = "♦"
177+ best_n = n
178+ }
179+
180+ // FIXME: Should take into account what their opponent has done
181+
182+ // Pick one we think can win with
183+ var partner_n = 0.5
184+ if (best_n + partner_n >= 3)
185+ return trump_suit
186+ else
187+ return ""
188+}
189+
190+// Pick a card for the AI to play
191+AI.prototype.pick_card = function (trick) {
192+ // Lead with the highest off-suit, or trump if that's all you have!
193+ if (trick.length == 0) {
194+ // FIXME: Take into account which cards have been played, i.e. King might be the highest
195+ // FIXME: Pick between suits based on if they have been led, and if you can de-suit
196+ var highest_trump
197+ var highest_trump_value = -1
198+ var highest_non_trump
199+ var highest_non_trump_value = -1
200+ for (var i = 0; i < this.hand.length; i++) {
201+ var card = this.hand[i]
202+ var v = Util.get_card_value (card)
203+ if (Util.is_trump (card)) {
204+ if (v > highest_trump_value) {
205+ highest_trump = card
206+ highest_trump_value = v
207+ }
208+ }
209+ else {
210+ if (v > highest_non_trump_value) {
211+ highest_non_trump = card
212+ highest_non_trump_value = v
213+ }
214+ }
215+ }
216+ if (highest_non_trump != undefined)
217+ return highest_non_trump
218+ else
219+ return highest_trump
220+ }
221+
222+ // Work out which cards are playable (follow suit or anything)
223+ var playable_cards = []
224+ var lead_suit = Util.get_suit (trick[0])
225+ for (var i = 0; i < this.hand.length; i++)
226+ if (Util.get_suit (this.hand[i]) == lead_suit)
227+ playable_cards.push (this.hand[i])
228+ if (playable_cards.length == 0)
229+ playable_cards = this.hand
230+
231+ var winning_card = Util.get_winning_card (trick)
232+ var winning_card_value = Util.get_card_value (winning_card)
233+
234+ // Work out various useful cards we can play
235+ var lowest_in_suit
236+ var lowest_in_suit_value = 8
237+ var lowest_winning
238+ var lowest_winning_value = 8
239+ var highest_winning
240+ var highest_winning_value = -1
241+ var lowest_trump
242+ var lowest_trump_value = 8
243+ var lowest_non_trump
244+ var lowest_non_trump_value = 8
245+ for (var i = 0; i < playable_cards.length; i++) {
246+ var card = playable_cards[i]
247+ var v = Util.get_card_value (card)
248+
249+ if (Util.get_suit (card) == lead_suit) {
250+ if (v < lowest_in_suit_value) {
251+ lowest_in_suit = card
252+ lowest_in_suit_value = v
253+ }
254+ }
255+
256+ if (Util.get_suit (card) == Util.get_suit (winning_card) && v > winning_card_value) {
257+ if (v < lowest_winning_value) {
258+ lowest_winning = card
259+ lowest_winning_value = v
260+ }
261+ if (v > highest_winning_value) {
262+ highest_winning = card
263+ highest_winning_value = v
264+ }
265+ }
266+
267+ if (Util.is_trump (card)) {
268+ if (v < lowest_trump_value) {
269+ lowest_trump = card
270+ lowest_trump_value = v
271+ }
272+ }
273+ else {
274+ if (v < lowest_non_trump_value) {
275+ lowest_non_trump = card
276+ lowest_non_trump_value = v
277+ }
278+ }
279+ }
280+
281+ // Can only trump if we can't follow suit
282+ var lowest_winning_trump
283+ if (lowest_in_suit == undefined && !Util.is_trump (winning_card))
284+ lowest_winning_trump = lowest_trump
285+
286+ // Get the card we should throw away if can't do anything
287+ // FIXME: Should try to de-suit
288+ var discard = lowest_non_trump
289+ if (discard == undefined)
290+ discard = lowest_trump
291+
292+ // Following opponent - play highest card in that suit or lowest trump
293+ // FIXME: Shouldn't play a high card if we think it will be more useful later
294+ if (trick.length == 1) {
295+ if (highest_winning != undefined)
296+ return highest_winning
297+ else if (lowest_winning_trump != undefined)
298+ return lowest_winning_trump
299+ else
300+ return discard
301+ }
302+
303+ // Following partner, win if they haven't, otherwise play lowest card
304+ if (trick.length == 2) {
305+ // FIXME: They might be winning but need us to play higher
306+ var partner_won = trick[0] == winning_card
307+ if (partner_won) {
308+ if (lowest_in_suit != undefined)
309+ return lowest_in_suit
310+ else
311+ return discard
312+ }
313+ else {
314+ if (highest_winning != undefined)
315+ return highest_winning
316+ else if (lowest_winning_trump != undefined)
317+ return lowest_winning_trump
318+ else
319+ return discard
320+ }
321+ }
322+
323+ // Ending trick, if partner is winning play lowest card, otherwise win with with lowest possible card
324+ if (trick.length == 3) {
325+ var partner_won = trick[1] == winning_card
326+ if (partner_won) {
327+ if (lowest_in_suit != undefined)
328+ return lowest_in_suit
329+ else
330+ return discard
331+ }
332+ else {
333+ if (lowest_winning != undefined)
334+ return lowest_winning
335+ if (lowest_winning_trump != undefined)
336+ return lowest_winning_trump
337+ else
338+ return discard
339+ }
340+ }
341+}
342
343=== added directory 'cards'
344=== renamed file 'aceClubs.png' => 'cards/aceClubs.png'
345=== renamed file 'aceDiamonds.png' => 'cards/aceDiamonds.png'
346=== renamed file 'aceHearts.png' => 'cards/aceHearts.png'
347=== renamed file 'aceSpades.png' => 'cards/aceSpades.png'
348=== renamed file 'back.png' => 'cards/back.png'
349=== renamed file 'jackClubs.png' => 'cards/jackClubs.png'
350=== renamed file 'jackDiamonds.png' => 'cards/jackDiamonds.png'
351=== renamed file 'jackHearts.png' => 'cards/jackHearts.png'
352=== renamed file 'jackSpades.png' => 'cards/jackSpades.png'
353=== renamed file 'kingClubs.png' => 'cards/kingClubs.png'
354=== renamed file 'kingDiamonds.png' => 'cards/kingDiamonds.png'
355=== renamed file 'kingHearts.png' => 'cards/kingHearts.png'
356=== renamed file 'kingSpades.png' => 'cards/kingSpades.png'
357=== renamed file 'nineClubs.png' => 'cards/nineClubs.png'
358=== renamed file 'nineDiamonds.png' => 'cards/nineDiamonds.png'
359=== renamed file 'nineHearts.png' => 'cards/nineHearts.png'
360=== renamed file 'nineSpades.png' => 'cards/nineSpades.png'
361=== renamed file 'queenClubs.png' => 'cards/queenClubs.png'
362=== renamed file 'queenDiamonds.png' => 'cards/queenDiamonds.png'
363=== renamed file 'queenHearts.png' => 'cards/queenHearts.png'
364=== renamed file 'queenSpades.png' => 'cards/queenSpades.png'
365=== renamed file 'tenClubs.png' => 'cards/tenClubs.png'
366=== renamed file 'tenDiamonds.png' => 'cards/tenDiamonds.png'
367=== renamed file 'tenHearts.png' => 'cards/tenHearts.png'
368=== renamed file 'tenSpades.png' => 'cards/tenSpades.png'
369=== modified file 'main.qml'
370--- main.qml 2015-03-10 06:45:40 +0000
371+++ main.qml 2015-03-11 05:38:33 +0000
372@@ -10,6 +10,8 @@
373
374 import QtQuick 2.0
375 import Ubuntu.Components 1.1
376+import "ai_basic.js" as AI
377+import "util.js" as Util
378
379 MainView {
380 applicationName: "com.ubuntu.developer.robert-ancell.euchre"
381@@ -152,8 +154,8 @@
382 var card = card_component.createObject (table)
383 card.number = number_symbols[n]
384 card.suit = suit_symbols[s]
385- card.front_source = numbers[n] + suits[s] + ".png"
386- card.back_source = "back.png"
387+ card.front_source = "cards/" + numbers[n] + suits[s] + ".png"
388+ card.back_source = "cards/back.png"
389 deck.push (card)
390 }
391 }
392@@ -164,13 +166,13 @@
393 var card = card_component.createObject (table)
394 card.opacity = 0
395 card.suit = suit_symbols[s]
396- card.front_source = "ace" + suits[s] + ".png"
397+ card.front_source = "cards/ace" + suits[s] + ".png"
398 suit_cards.push (card)
399 }
400 var trumpSkip = card_component.createObject (table)
401 trumpSkip.opacity = 0
402 trumpSkip.suit = ""
403- trumpSkip.front_source = "back.png"
404+ trumpSkip.front_source = "cards/back.png"
405 suit_cards.push (trumpSkip)
406
407 // Handle the user clicking on the cards
408@@ -212,6 +214,9 @@
409 // The current trick
410 var trick = []
411
412+ // AIs. The first entry is null, since that corresponds to the human player
413+ var AIs = [ null, new AI.AI(w_hand), new AI.AI(n_hand), new AI.AI(e_hand) ]
414+
415 // Player who dealt this round
416 var dealer = 0
417
418@@ -224,9 +229,6 @@
419 // The player that won the call
420 var calling_player = -1
421
422- // Trump suit for this trick
423- var trump_suit = ""
424-
425 // Game scores
426 var ew_score = 0
427 var ns_score = 0
428@@ -650,7 +652,7 @@
429 current_player = dealer
430 lead_player = 0
431 calling_player = -1
432- trump_suit = ""
433+ Util.trump_suit = ""
434 trump_label.opacity = 0
435 maker_arrow.opacity = 0
436
437@@ -667,13 +669,17 @@
438 }
439 shuffle (deck)
440
441- n_hand = [ deck[0], deck[1], deck[2], deck[3], deck[4] ]
442+ n_hand.length = 0
443+ n_hand.push (deck[0], deck[1], deck[2], deck[3], deck[4])
444 n_tricks = []
445- e_hand = [ deck[5], deck[6], deck[7], deck[8], deck[9] ]
446+ e_hand.length = 0
447+ e_hand.push (deck[5], deck[6], deck[7], deck[8], deck[9])
448 e_tricks = []
449- s_hand = [ deck[10], deck[11], deck[12], deck[13], deck[14] ]
450+ s_hand.length = 0
451+ s_hand.push (deck[10], deck[11], deck[12], deck[13], deck[14])
452 s_tricks = []
453- w_hand = [ deck[15], deck[16], deck[17], deck[18], deck[19] ]
454+ w_hand.length = 0
455+ w_hand.push (deck[15], deck[16], deck[17], deck[18], deck[19])
456 w_tricks = []
457 kitty = [ deck[20], deck[21], deck[22], deck[23] ]
458 trick = []
459@@ -754,9 +760,9 @@
460 }
461
462 calling_player = current_player
463- trump_suit = kitty[3].suit
464+ Util.trump_suit = kitty[3].suit
465 trump_label.opacity = 0.25
466- trump_label.text = trump_suit
467+ trump_label.text = Util.trump_suit
468 maker_arrow.opacity = 0.25
469 maker_arrow.rotation = 90 * (calling_player + 1)
470
471@@ -777,7 +783,7 @@
472
473 // Swap cards
474 console.log (player_name (dealer) + " swaps")
475- var card = ai_pick_swap (dealer)
476+ var card = AIs[dealer].pick_swap (kitty[3])
477 if (card != undefined) {
478 var hand = player_hand (dealer)
479 var hand_index = hand.indexOf (card)
480@@ -844,11 +850,11 @@
481 trump_label.opacity = 0.25
482 trump_label.text = suit
483 calling_player = current_player
484- trump_suit = suit
485+ Util.trump_suit = suit
486
487 // Raise the chosen card to the top and fade out the others
488 for (var i = 0; i < suit_cards.length; i++) {
489- if (suit_cards[i].suit == trump_suit) {
490+ if (suit_cards[i].suit == Util.trump_suit) {
491 var t = suit_cards[i].z
492 suit_cards[i].z = suit_cards[suit_cards.length - 1].z
493 suit_cards[suit_cards.length - 1].z = t
494@@ -890,329 +896,6 @@
495 layout ()
496 }
497
498- // Get an estimate on the number of tricks we will win
499- function ai_get_hand_strength (player, trump_suit) {
500- var hand = player_hand (player)
501- function have_card (number, suit) {
502- for (var i = 0; i < hand.length; i++)
503- if (hand[i].number == number && hand[i].suit == suit)
504- return true
505- return false
506- }
507- function suit_count (suit) {
508- var n = 0
509- for (var i = 0; i < hand.length; i++) {
510- var s = hand[i].suit
511- if (hand[i].number == "J" && hand[i].suit == get_off_suit (trump_suit))
512- s = trump_suit
513- if (s == suit)
514- n++
515- }
516- return n
517- }
518-
519- // FIXME: Should take into account if a card has been turned down (i.e. treat King offsuit as Ace offsuit if Ace was discarded)
520-
521- // Count how many consecutive top trumps we have
522- var n_top_trumps = 0
523- if (have_card ("J", trump_suit)) {
524- n_top_trumps++
525- if (have_card ("J", get_off_suit (trump_suit))) {
526- n_top_trumps++
527- if (have_card ("A", trump_suit)) {
528- n_top_trumps++
529- if (have_card ("K", trump_suit)) {
530- n_top_trumps++
531- if (have_card ("Q", trump_suit))
532- n_top_trumps++
533- }
534- }
535- }
536- }
537-
538- // Might get something with other trumps
539- var n_other_trumps = suit_count (trump_suit) - n_top_trumps
540-
541- // Count how many Ace off-suits we have
542- var n_ace_offsuits = 0
543- if (trump_suit != "♠" && have_card ("A", "♠"))
544- n_ace_offsuits++
545- if (trump_suit != "♣" && have_card ("A", "♣"))
546- n_ace_offsuits++
547- if (trump_suit != "♥" && have_card ("A", "♥"))
548- n_ace_offsuits++
549- if (trump_suit != "♦" && have_card ("A", "♦"))
550- n_ace_offsuits++
551-
552- // Kings and queens might give something if there are other cards to help
553- var n_king_offsuits = 0
554- if (trump_suit != "♠" && have_card ("K", "♠") && suit_count ("♠") > 1)
555- n_king_offsuits++
556- if (trump_suit != "♣" && have_card ("K", "♣") && suit_count ("♣") > 1)
557- n_king_offsuits++
558- if (trump_suit != "♥" && have_card ("K", "♥") && suit_count ("♥") > 1)
559- n_king_offsuits++
560- if (trump_suit != "♦" && have_card ("K", "♦") && suit_count ("♦") > 1)
561- n_king_offsuits++
562- var n_queen_offsuits = 0
563- if (trump_suit != "♠" && have_card ("Q", "♠") && suit_count ("♠") > 2)
564- n_queen_offsuits++
565- if (trump_suit != "♣" && have_card ("Q", "♣") && suit_count ("♣") > 2)
566- n_queen_offsuits++
567- if (trump_suit != "♥" && have_card ("Q", "♥") && suit_count ("♥") > 2)
568- n_queen_offsuits++
569- if (trump_suit != "♦" && have_card ("Q", "♦") && suit_count ("♦") > 2)
570- n_queen_offsuits++
571-
572- // FIXME: Should simulate and find the best values for these weightings
573- return n_top_trumps + n_other_trumps * 0.5 + n_ace_offsuits + n_king_offsuits * 0.5 + n_queen_offsuits * 0.5
574- }
575-
576- // Return true if the AI will order the dealer
577- function ai_will_order (player) {
578- // If the dealer is our partner then expect more from then
579- // FIXME: Should simulate and find the best values for these weightings
580- var partner_n = 0.5
581- if (player == (dealer + 2) % 4) {
582- if (kitty[3].number == "J")
583- partner_n = 1.5
584- else
585- partner_n = 0.75
586- }
587-
588- // Work out how strong we are with this trump
589- var hand = player_hand (player)
590- var n
591- var swap
592- var swap_index
593- if (player == dealer) {
594- swap = ai_pick_swap (player)
595- if (swap != undefined) {
596- swap_index = hand.indexOf (swap)
597- hand[swap_index] = kitty[3]
598- }
599- }
600- n = ai_get_hand_strength (player, kitty[3].suit)
601- if (swap != undefined)
602- hand[swap_index] = swap
603-
604- // Order if we think we can win this round
605- return n + partner_n >= 3
606- }
607-
608- // Pick a card to swap when ordered
609- function ai_pick_swap (player) {
610- var hand = player_hand (player)
611- var swap_card
612- var swap_strength = -1
613- for (var i = 0; i < hand.length; i++) {
614- // Temporarily swap card
615- var t = hand[i]
616- hand[i] = kitty[3]
617-
618- var s = ai_get_hand_strength (player, "", kitty[3].suit)
619- if (s > swap_strength) {
620- swap_card = t
621- swap_strength = s
622- }
623-
624- // Swap card back
625- hand[i] = t
626- }
627-
628- return swap_card
629- }
630-
631- // Pick a trump suit to play as or "" to pass
632- function ai_pick_trump (player) {
633- // Try each suit and pick the best one
634- var trump_suit = ""
635- var best_n = 0
636- var n = ai_get_hand_strength (player, "♠")
637- if (n > best_n) {
638- trump_suit = "♠"
639- best_n = n
640- }
641- var n = ai_get_hand_strength (player, "♣")
642- if (n > best_n) {
643- trump_suit = "♣"
644- best_n = n
645- }
646- var n = ai_get_hand_strength (player, "♥")
647- if (n > best_n) {
648- trump_suit = "♥"
649- best_n = n
650- }
651- var n = ai_get_hand_strength (player, "♦")
652- if (n > best_n) {
653- trump_suit = "♦"
654- best_n = n
655- }
656-
657- // FIXME: Should take into account what their opponent has done
658-
659- // Pick one we think can win with
660- var partner_n = 0.5
661- if (best_n + partner_n >= 3)
662- return trump_suit
663- else
664- return ""
665- }
666-
667- // Pick a card for the AI to play
668- function ai_pick_card (player) {
669- var hand = player_hand (player)
670-
671- // Lead with the highest off-suit, or trump if that's all you have!
672- if (trick.length == 0) {
673- // FIXME: Take into account which cards have been played, i.e. King might be the highest
674- // FIXME: Pick between suits based on if they have been led, and if you can de-suit
675- var highest_trump
676- var highest_trump_value = -1
677- var highest_non_trump
678- var highest_non_trump_value = -1
679- for (var i = 0; i < hand.length; i++) {
680- var card = hand[i]
681- var v = get_card_value (card)
682- if (is_trump (card)) {
683- if (v > highest_trump_value) {
684- highest_trump = card
685- highest_trump_value = v
686- }
687- }
688- else {
689- if (v > highest_non_trump_value) {
690- highest_non_trump = card
691- highest_non_trump_value = v
692- }
693- }
694- }
695- if (highest_non_trump != undefined)
696- return highest_non_trump
697- else
698- return highest_trump
699- }
700-
701- // Work out which cards are playable (follow suit or anything)
702- var playable_cards = []
703- var lead_suit = get_suit (trick[0])
704- for (var i = 0; i < hand.length; i++)
705- if (get_suit (hand[i]) == lead_suit)
706- playable_cards.push (hand[i])
707- if (playable_cards.length == 0)
708- playable_cards = hand
709-
710- var winning_card = get_winning_card ()
711- var winning_card_value = get_card_value (winning_card)
712-
713- // Work out various useful cards we can play
714- var lowest_in_suit
715- var lowest_in_suit_value = 8
716- var lowest_winning
717- var lowest_winning_value = 8
718- var highest_winning
719- var highest_winning_value = -1
720- var lowest_trump
721- var lowest_trump_value = 8
722- var lowest_non_trump
723- var lowest_non_trump_value = 8
724- for (var i = 0; i < playable_cards.length; i++) {
725- var card = playable_cards[i]
726- var v = get_card_value (card)
727-
728- if (get_suit (card) == lead_suit) {
729- if (v < lowest_in_suit_value) {
730- lowest_in_suit = card
731- lowest_in_suit_value = v
732- }
733- }
734-
735- if (get_suit (card) == get_suit (winning_card) && v > winning_card_value) {
736- if (v < lowest_winning_value) {
737- lowest_winning = card
738- lowest_winning_value = v
739- }
740- if (v > highest_winning_value) {
741- highest_winning = card
742- highest_winning_value = v
743- }
744- }
745-
746- if (is_trump (card)) {
747- if (v < lowest_trump_value) {
748- lowest_trump = card
749- lowest_trump_value = v
750- }
751- }
752- else {
753- if (v < lowest_non_trump_value) {
754- lowest_non_trump = card
755- lowest_non_trump_value = v
756- }
757- }
758- }
759-
760- // Can only trump if we can't follow suit
761- var lowest_winning_trump
762- if (lowest_in_suit == undefined && !is_trump (winning_card))
763- lowest_winning_trump = lowest_trump
764-
765- // Get the card we should throw away if can't do anything
766- // FIXME: Should try to de-suit
767- var discard = lowest_non_trump
768- if (discard == undefined)
769- discard = lowest_trump
770-
771- // Following opponent - play highest card in that suit or lowest trump
772- // FIXME: Shouldn't play a high card if we think it will be more useful later
773- if (trick.length == 1) {
774- if (highest_winning != undefined)
775- return highest_winning
776- else if (lowest_winning_trump != undefined)
777- return lowest_winning_trump
778- else
779- return discard
780- }
781-
782- // Following partner, win if they haven't, otherwise play lowest card
783- if (trick.length == 2) {
784- // FIXME: They might be winning but need us to play higher
785- var partner_won = trick[0] == winning_card
786- if (partner_won) {
787- if (lowest_in_suit != undefined)
788- return lowest_in_suit
789- else
790- return discard
791- }
792- else {
793- if (highest_winning != undefined)
794- return highest_winning
795- else if (lowest_winning_trump != undefined)
796- return lowest_winning_trump
797- else
798- return discard
799- }
800- }
801-
802- // Ending trick, if partner is winning play lowest card, otherwise win with with lowest possible card
803- if (trick.length == 3) {
804- var partner_won = trick[1] == winning_card
805- if (partner_won) {
806- if (lowest_in_suit != undefined)
807- return lowest_in_suit
808- else
809- return discard
810- }
811- else {
812- if (lowest_winning != undefined)
813- return lowest_winning
814- if (lowest_winning_trump != undefined)
815- return lowest_winning_trump
816- else
817- return discard
818- }
819- }
820- }
821
822 // Play the given card
823 function play_card (card) {
824@@ -1414,9 +1097,9 @@
825 // Must follow suit
826 var playable_cards = []
827 if (trick.length > 0) {
828- var lead_suit = get_suit (trick[0])
829+ var lead_suit = Util.get_suit (trick[0])
830 for (var i = 0; i < s_hand.length; i++)
831- if (get_suit (s_hand[i]) == lead_suit)
832+ if (Util.get_suit (s_hand[i]) == lead_suit)
833 playable_cards.push (s_hand[i])
834 }
835 if (playable_cards.length > 0 && playable_cards.indexOf (card) < 0) {
836@@ -1457,64 +1140,6 @@
837 set_trump (suit)
838 }
839
840- // Get the suit that contains the left bower
841- function get_off_suit (suit) {
842- if (suit == "♠")
843- return "♣"
844- if (suit == "♣")
845- return "♠"
846- if (suit == "♥")
847- return "♦"
848- if (suit == "♦")
849- return "♥"
850- }
851-
852- // Get the suit of a card taking into account bowers
853- function get_suit (card) {
854- if (card.suit == get_off_suit (trump_suit) && card.number == "J")
855- return trump_suit
856- return card.suit
857- }
858-
859- // Return true if the card has the given suit
860- function is_suit (card, suit) {
861- return get_suit (card) == suit
862- }
863-
864- // Return true if the card is a trump
865- function is_trump (card) {
866- return get_suit (card) == trump_suit
867- }
868-
869- // Get the value of a card within it's own suit
870- function get_card_value (card) {
871- if (card.number == "9")
872- return 0
873- if (card.number == "10")
874- return 1
875- if (card.number == "J")
876- {
877- // Right bower
878- if (card.suit == trump_suit)
879- return 7
880-
881- // Left bower
882- if (card.suit == get_off_suit (trump_suit))
883- return 6
884-
885- // Plain old Jack
886- return 2
887- }
888- if (card.number == "Q")
889- return 3
890- if (card.number == "K")
891- return 4
892- if (card.number == "A")
893- return 5
894-
895- return -1
896- }
897-
898 // Start the next turn
899 function next_turn () {
900 current_player = (current_player + 1) % 4
901@@ -1548,7 +1173,8 @@
902
903 // AI chooses if it wants to order the dealer
904 phase = "ordering"
905- if (ai_will_order (current_player))
906+ // Ensure argument is positive, since % is broken in javascript
907+ if (AIs[current_player].will_order ((4 + dealer - current_player) % 4, kitty[3]))
908 order_dealer ()
909 else
910 pass_order ()
911@@ -1576,7 +1202,7 @@
912
913 // AI chooses if it wants to order the dealer
914 phase = "calling"
915- var suit = ai_pick_trump (current_player)
916+ var suit = AIs[current_player].pick_trump ()
917 set_trump (suit)
918 }
919 card.moved.connect (moved)
920@@ -1586,38 +1212,9 @@
921 }
922 }
923
924- // Get the winning card from the current trick
925- function get_winning_card () {
926- function compare_card (card0, card1, lead_suit) {
927- if (get_suit (card0) != get_suit (card1))
928- {
929- // Trumps beat non-trumps
930- if (is_suit (card0, trump_suit))
931- return 1
932- if (is_suit (card1, trump_suit))
933- return -1
934-
935- // Lead suit beats off-suit
936- if (is_suit (card0, lead_suit))
937- return 1
938- if (is_suit (card1, lead_suit))
939- return -1
940- }
941-
942- return get_card_value (card0) - get_card_value (card1)
943- }
944- var lead_suit = trick[0].suit
945- var best_card = 0
946- for (var i = 1; i < trick.length; i++)
947- if (compare_card (trick[i], trick[best_card], lead_suit, trump_suit) > 0)
948- best_card = i
949-
950- return trick[best_card]
951- }
952-
953 // Get the player currently winning the trick
954 function trick_winner () {
955- var card = get_winning_card ()
956+ var card = Util.get_winning_card (trick)
957 return (lead_player + trick.indexOf (card)) % 4
958 }
959
960@@ -1630,7 +1227,7 @@
961 }
962 else {
963 // FIXME: Pretend to be thinking
964- var card = ai_pick_card (current_player)
965+ var card = AIs[current_player].pick_card (trick)
966 console.log (player_name (current_player) + " plays " + card.number + card.suit)
967 play_card (card)
968 var hand = player_hand (current_player)
969
970=== added file 'util.js'
971--- util.js 1970-01-01 00:00:00 +0000
972+++ util.js 2015-03-11 05:38:33 +0000
973@@ -0,0 +1,100 @@
974+/*
975+ * Copyright (C) 2013 Robert Ancell <robert.ancell@gmail.com>
976+ *
977+ * This program is free software: you can redistribute it and/or modify it under
978+ * the terms of the GNU General Public License as published by the Free Software
979+ * Foundation, either version 3 of the License, or (at your option) any later
980+ * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
981+ * license.
982+ */
983+
984+.pragma library
985+
986+var trump_suit
987+
988+// Get the suit that contains the left bower
989+function get_off_suit (suit) {
990+ if (suit == "♠")
991+ return "♣"
992+ if (suit == "♣")
993+ return "♠"
994+ if (suit == "♥")
995+ return "♦"
996+ if (suit == "♦")
997+ return "♥"
998+}
999+
1000+// Get the suit of a card taking into account bowers
1001+function get_suit (card) {
1002+ if (card.suit == get_off_suit (trump_suit) && card.number == "J")
1003+ return trump_suit
1004+ return card.suit
1005+}
1006+
1007+// Return true if the card has the given suit
1008+function is_suit (card, suit) {
1009+ return get_suit (card) == suit
1010+}
1011+
1012+// Return true if the card is a trump
1013+function is_trump (card) {
1014+ return get_suit (card) == trump_suit
1015+}
1016+
1017+// Get the value of a card within it's own suit
1018+function get_card_value (card) {
1019+ if (card.number == "9")
1020+ return 0
1021+ if (card.number == "10")
1022+ return 1
1023+ if (card.number == "J")
1024+ {
1025+ // Right bower
1026+ if (card.suit == trump_suit)
1027+ return 7
1028+
1029+ // Left bower
1030+ if (card.suit == get_off_suit (trump_suit))
1031+ return 6
1032+
1033+ // Plain old Jack
1034+ return 2
1035+ }
1036+ if (card.number == "Q")
1037+ return 3
1038+ if (card.number == "K")
1039+ return 4
1040+ if (card.number == "A")
1041+ return 5
1042+
1043+ return -1
1044+}
1045+
1046+// Get the winning card from the current trick
1047+function get_winning_card (trick) {
1048+ function compare_card (card0, card1, lead_suit) {
1049+ if (get_suit (card0) != get_suit (card1))
1050+ {
1051+ // Trumps beat non-trumps
1052+ if (is_suit (card0, trump_suit))
1053+ return 1
1054+ if (is_suit (card1, trump_suit))
1055+ return -1
1056+
1057+ // Lead suit beats off-suit
1058+ if (is_suit (card0, lead_suit))
1059+ return 1
1060+ if (is_suit (card1, lead_suit))
1061+ return -1
1062+ }
1063+
1064+ return get_card_value (card0) - get_card_value (card1)
1065+ }
1066+ var lead_suit = trick[0].suit
1067+ var best_card = 0
1068+ for (var i = 1; i < trick.length; i++)
1069+ if (compare_card (trick[i], trick[best_card], lead_suit, trump_suit) > 0)
1070+ best_card = i
1071+
1072+ return trick[best_card]
1073+}

Subscribers

People subscribed via source and target branches