func (t *testSuite) TestBidding() { // 9D 9D QD TD TD AD JC QC KC AH AH KS hand := sdz.Hand{C("9D"), C("9D"), C("QD"), C("TD"), C("TD"), C("AD"), C("JC"), C("QC"), C("KC"), C("AH"), C("AH"), C("KS")} sort.Sort(hand) ai := createAI() ai.SetHand(hand, 0, 1) go ai.Go() ai.Tell(sdz.CreateBid(0, 1)) action, _ := ai.Listen() t.Not(t.True(22 > action.Bid || action.Bid > 24)) }
func (ai *AI) Go() { for { action, open := ai.Listen() //Log("Action received by player %d with hand %s - %+v", ai.Playerid(), ai.hand, action) if !open { return } switch action.Type { case "Bid": if action.Playerid == ai.Playerid() { Log("------------------Player %d asked to bid against player %d", ai.Playerid(), ai.highBidder) ai.bidAmount, ai.trump, ai.show = ai.calculateBid() if ai.numBidders == 1 && ai.IsPartner(ai.highBidder) && ai.bidAmount < 21 && ai.bidAmount+5 > 20 { // save our parter Log("Saving our partner with a recommended bid of %d", ai.bidAmount) ai.bidAmount = 21 } bidAmountOld := ai.bidAmount switch { case ai.Playerid() == ai.highBidder: // this should only happen if I was the dealer and I got stuck ai.bidAmount = 20 case ai.highBid > ai.bidAmount: ai.bidAmount = 0 case ai.highBid == ai.bidAmount && !ai.IsPartner(ai.highBidder): // if equal with an opponent, bid one over them for spite! ai.bidAmount++ case ai.numBidders == 3: // I'm last to bid, but I want it ai.bidAmount = ai.highBid + 1 } meld, _ := ai.hand.Meld(ai.trump) Log("------------------Player %d bid %d over %d with recommendation of %d and %d meld", ai.Playerid(), ai.bidAmount, ai.highBid, bidAmountOld, meld) ai.c <- sdz.CreateBid(ai.bidAmount, ai.Playerid()) } else { // received someone else's bid value' if ai.highBid < action.Bid { ai.highBid = action.Bid ai.highBidder = action.Playerid } ai.numBidders++ } case "Play": if action.Playerid == ai.Playerid() { decisionMap := make(map[sdz.Card]int) var selection sdz.Card for _, card := range *ai.hand { if ai.playCount == 0 || sdz.ValidPlay(card, action.WinningCard, action.Lead, ai.hand, action.Trump) { decisionMap[card] = 1 selection = card } } for card := range decisionMap { if ai.playCount == 0 { // TODO: Anticipate opponents trumping in if card.Face() == sdz.Ace { // choose the Ace with the least amount of cards in the suit decisionMap[card] += 12 - ai.hand.CountSuit(card.Suit()) } } else if card.Beats(action.WinningCard, action.Trump) { if ai.hand.Highest(card) { decisionMap[card]++ } } else if ai.playCount > 0 && action.WinningPlayer%2 == ai.Playerid()%2 { if card.Counter() { decisionMap[card]++ } else { decisionMap[card]-- } } else { // TODO: Anticipate partner winning the hand through trump or otherwise if ai.hand.Lowest(card) { decisionMap[card]++ } } if decisionMap[card] > decisionMap[selection] { selection = card } } Log("%d - Playing %s - Decision map = %v", ai.Playerid(), selection, decisionMap) action = sdz.CreatePlay(selection, ai.Playerid()) ai.ht.playedCards[action.PlayedCard]++ ai.c <- action } else { ai.playCount++ ai.ht.playedCards[action.PlayedCard]++ // TODO: Keep track of what has been played already // received someone else's play } case "Trump": if action.Playerid == ai.Playerid() { meld, _ := ai.hand.Meld(ai.trump) Log("Player %d being asked to name trump on hand %s and have %d meld", ai.Playerid(), ai.hand, meld) switch { // TODO add case for the end of the game like if opponents will coast out case ai.bidAmount < 15: ai.c <- sdz.CreateThrowin(ai.Playerid()) default: ai.c <- sdz.CreateTrump(ai.trump, ai.Playerid()) } } else { //Log("Player %d was told trump", ai.Playerid()) ai.trump = action.Trump } case "Throwin": Log("Player %d saw that player %d threw in", ai.Playerid(), action.Playerid) case "Deal": // nothing to do here, this is set automagically case "Meld": // nothing to do here, no one to read it case "Message": // nothing to do here, no one to read it case "Trick": // nothing to do here, nothing to display ai.playCount = 0 case "Score": // TODO: save score to use for future bidding techniques default: Log("Received an action I didn't understand - %v", action) } } }
func main() { autoClient := false for _, s := range os.Args { fmt.Println(s) if s == "auto" { autoClient = true } } conn, err := websocket.Dial("ws://localhost:10080/connect", "", "http://localhost:10080/") var playerid int var hand *sdz.Hand var bidAmount int var trump sdz.Suit if err != nil { sdz.Log("Error - %v", err) return } defer conn.Close() var previousPlay *sdz.Action var previousCard sdz.Card for { var action sdz.Action err := websocket.JSON.Receive(conn, &action) if err != nil { sdz.Log("Error decoding - %v", err) return } switch action.Type { case "Bid": if action.Playerid == playerid { sdz.Log("How much would you like to bid?:") fmt.Scan(&bidAmount) send(conn, sdz.CreateBid(bidAmount, playerid)) } else { // received someone else's bid value' sdz.Log("Player #%d bid %d", action.Playerid, action.Bid) } case "Play": if action.Playerid == playerid { if previousPlay == nil || action.Lead == "" { previousPlay = &action } else { sdz.Log("Server rejected the play of %s, invalid play", previousCard) *hand = append(*hand, previousCard) sort.Sort(hand) } var card sdz.Card if action.Lead == "" { card = (*hand)[0] } else { for _, c := range *hand { if sdz.ValidPlay(c, action.WinningCard, action.Lead, hand, trump) { card = c } } } sdz.Log("Your turn, in your hand is %s - what would you like to play? Trump is %s - valid play is %s:", hand, trump, card) for { if !autoClient { fmt.Scan(&card) } //sdz.Log("Received input %s", card) if hand.Remove(card) { send(conn, sdz.CreatePlay(card, playerid)) previousCard = card break } else { sdz.Log("Invalid play, card does not exist in your hand") } } } else { sdz.Log("Player %d played card %s", action.Playerid, action.PlayedCard) previousPlay = nil // not going to ask us to replay since the next response was another player's play // received someone else's play' } case "Trump": if action.Playerid == playerid { sdz.Log("What would you like to make trump?") fmt.Scan(&trump) send(conn, sdz.CreateTrump(trump, playerid)) } else { sdz.Log("Player %d says trump is %s", action.Playerid, action.Trump) trump = action.Trump } case "Throwin": sdz.Log("Player %d threw in", action.Playerid) case "Deal": playerid = action.Playerid hand = &action.Hand sdz.Log("Your hand is - %s", hand) case "Meld": sdz.Log("Player %d is melding %s for %d points", action.Playerid, action.Hand, action.Amount) case "Score": // this client does not have to implement this type as it's already told through Message actions case "Message": sdz.Log(action.Message) case "Hello": var response string fmt.Scan(&response) send(conn, sdz.CreateHello(response)) case "Game": var option int fmt.Scan(&option) send(conn, sdz.CreateGame(option)) default: sdz.Log("Received an action I didn't understand - %v", action) } } }