///////////////////////////////////////////////////////////////////////////// // Name: game.cpp // tag: Implement a bridge window with all the visuals // Author: David Roundy // Modified by: // Created: 9/2001 // Copyright: (c) 2001 David Roundy // Licence: GPL /* 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 */ ///////////////////////////////////////////////////////////////////////////// // For compilers that support precompilation, includes . #include #ifndef WX_PRECOMP #include #include #endif #include "colors.h" #include "card.h" #include "cards.h" #include "game.h" #include "player.h" #include "bidwindow.h" #include "globals.h" #include "scorebase.h" #include "matchwin.h" #include "rubber.h" #include "prefmacros.h" #include "debug.h" BEGIN_EVENT_TABLE(aBridgeGame, wxPanel) EVT_MOUSE_EVENTS(aBridgeGame::OnMouseEvent) EVT_GAME(aBridgeGame::ProcessGameEvent) EVT_SET_FOCUS(aBridgeGame::SetTalkFocus) EVT_SIZE (aBridgeGame::HandleResize) END_EVENT_TABLE() aBridgeGame::aBridgeGame(wxWindow* parent, int x, int y, int w, int h) : wxPanel(parent, -1, wxPoint(x, y), (GetRememberWindowPositions())?GetGameSize():wxSize(w, h)) { #ifdef __WXGTK__ m_font = wxTheFontList->FindOrCreateFont(12, wxROMAN, wxNORMAL, wxNORMAL); #else m_font = wxTheFontList->FindOrCreateFont(10, wxSWISS, wxNORMAL, wxNORMAL); #endif SetBackgroundColour(aBridgeBackgroundColour()); SetForegroundColour(aBridgeTextColour()); SetFont(aBridgeFont()); for (int i=0;i<4;i++) { m_names[i] = ""; m_played_by_me[i] = FALSE; } m_dealer_seat = SOUTH; m_is_doubled = false; m_is_redoubled = false; m_scorer = new aBridgeRubber(this); m_hands[SOUTH] = new Hand(0,this, wxPoint(160,310),wxSize(281, 70)); m_hands[EAST] = new Hand(1,this, wxPoint(441, 80),wxSize( 50,270)); m_hands[NORTH] = new Hand(0,this, wxPoint(160, 80),wxSize(281, 70)); m_hands[WEST] = new Hand(1,this, wxPoint(110, 80),wxSize( 50,270)); m_played[SOUTH] = new wxCard(this, 0, faceup, wxPoint(275,235)); m_played[EAST] = new wxCard(this, 0, faceup, wxPoint(375,195)); m_played[NORTH] = new wxCard(this, 0, faceup, wxPoint(275,155)); m_played[WEST] = new wxCard(this, 0, faceup, wxPoint(175,195)); m_player[SOUTH] = new Player(this, SOUTH, 160, 380, 300, 110); m_player[EAST] = new Player(this, EAST, 490,120, 110, 200); m_player[NORTH] = new Player(this, NORTH, 160, 0, 300, 80); m_player[WEST] = new Player(this, WEST, 0 ,120, 110, 200); m_ns_trick_text = new wxStaticText(this, -1, "North South: ", wxPoint(5,30)); m_ew_trick_text = new wxStaticText(this, -1, "East West: ", wxPoint(5,50)); m_bid_text = new wxStaticText(this, -1, "", wxPoint(5,10)); m_bid_window = NULL; NewHand(); wxSizeEvent dummy; HandleResize(dummy); DebugMsg("Finished creating a new game"); } static inline double min(double a, double b) { return (aSetSize(cx - 150, h - 80, 300, 80); m_player[ EAST]->SetSize( w - 110, cy-100, 110, 200); m_player[NORTH]->SetSize(cx - 150, 0, 300, 80); m_player[ WEST]->SetSize( 0, cy-100, 110, 200); int mw = w - 2*110, mh = h - 2*80; double vhh_o_cw = 73*(1+0.25*12)/50.0; double hhw_o_cw = 1.0 + 0.2*12; double ch_o_cw = 73.0/50.0; int cw = (int) min(mw/(2+hhw_o_cw), mh/vhh_o_cw); printf("game (%d,%d) middle (%d,%d) cardwidth %d\n", w,h,mw,mh,cw); int ch = (int) (ch_o_cw * cw); m_hands[SOUTH]->SetSize(110 + cw,h-80-ch,mw-2*cw,ch); m_hands[WEST ]->SetSize( 110 , 80, cw,mh); m_hands[NORTH]->SetSize(110 + cw, 80,mw-2*cw,ch); m_hands[EAST ]->SetSize(w-110-cw, 80, cw,mh); int dy = (mh-2*ch)/4, dx = (mw-2*cw)/4; m_played[SOUTH]->SetSize(cx -cw/2,cy+dy-ch/2,cw,ch); m_played[EAST ]->SetSize(cx+dx-cw/2,cy -ch/2,cw,ch); m_played[NORTH]->SetSize(cx -cw/2,cy-dy-ch/2,cw,ch); m_played[WEST ]->SetSize(cx-dx-cw/2,cy -ch/2,cw,ch); } aBridgeGame::~aBridgeGame() { } void aBridgeGame::SetTalkFocus(wxFocusEvent& evt) { GetParent()->GetEventHandler()->ProcessEvent(evt); } bool aBridgeGame::HandleSit(const wxString& who, Seat seat, bool bootable) { if (!m_names[seat].IsSameAs("")) { if (bootable) { // You can't boot someone out and then substitute in their seat. return false; } HandleGetUp(seat); } m_names[seat] = who; if (m_played_by_me[seat]) m_played_by_me[seat] = false; m_player[seat]->HandleSit(who, bootable); return true; } void aBridgeGame::HandleGetUp(Seat seat) { if (m_played_by_me[seat] && m_whose_turn == seat && m_is_dealt && !m_is_bid) { // There is a bid board to be deleted! delete m_bid_window; } if (m_played_by_me[seat] && m_is_bid && seat != m_dummy_seat) { m_hands[seat]->TurnCards(facedown); } else if (m_played_by_me[seat] && !m_is_bid) { m_hands[seat]->TurnCards(facedown); } m_names[seat] = ""; m_played_by_me[seat] = false; m_hand_visible[seat] = false; m_player[seat]->HandleGetUp(); } void aBridgeGame::HandleMySit(const wxString &whoami, Seat seat, bool bootable) { m_names[seat] = whoami; if (m_hand_known[seat]) { m_hands[seat]->TurnCards(faceup); } m_played_by_me[seat] = true; m_player[seat]->HandleSit(whoami, bootable); if (m_is_dealt) { if (!m_is_bid) { if (m_whose_turn == seat) { m_bid_window = new BidWindow(this); m_bid_window->DisallowOneMove(); m_bid_window->Show(TRUE); } } } } void aBridgeGame::HandleBid(const wxString& who, const wxString& what) { m_player[m_whose_turn]->YourTurn(FALSE); m_player[m_whose_turn]->SayBid(what); int this_bid_done = 0; wxStringTokenizer tokens(what, " "); if (what.Matches("Pass")) { m_bid_values[m_num_bids] = 0; if (m_num_bids > 2) { if (!m_bid_values[m_num_bids-1] && !m_bid_values[m_num_bids-2]) { DebugMsg("The bid is finished!"); HandleBidFinished(); this_bid_done = 1; } } } else if (what.Matches("Double")) { m_bid_values[m_num_bids] = -1; m_is_doubled = TRUE; } else if (what.Matches("Redouble")) { m_bid_values[m_num_bids] = -2; m_is_redoubled = TRUE; } else { long temp; what.Left(' ').ToLong(&temp); m_bid_values[m_num_bids] = temp; m_bid_suits[m_num_bids] = String2Suit(what.AfterFirst(' ')); m_current_bid_val = temp; m_current_bid_suit = m_bid_suits[m_num_bids]; m_current_bid_winner = m_whose_turn; m_is_doubled = FALSE; m_is_redoubled = FALSE; } if (!this_bid_done) { m_num_bids++; m_whose_turn = RotateSeat(m_whose_turn); } m_player[m_whose_turn]->YourTurn(TRUE); if (!this_bid_done && m_played_by_me[m_whose_turn]) { bool can_double = (m_whose_turn + m_current_bid_winner) & 1; bool can_redouble = !can_double; if (m_is_doubled) can_double = FALSE; if (m_is_redoubled) can_redouble = FALSE; if (!m_is_doubled) can_redouble = FALSE; m_bid_window = new BidWindow(this, m_current_bid_val, m_current_bid_suit, can_double, can_redouble); m_bid_window->DisallowOneMove(); m_bid_window->Show(TRUE); } } void aBridgeGame::DebugSeatInfo() { int i; for (i=0;i<4;i++) { if (m_played_by_me[i]) { DebugMsg("I'm sitting in seat " + Seat2String((Seat) i)); } } DebugMsg("Dummy is sitting " + Seat2String(m_dummy_seat)); DebugMsg("Declarer is sitting " + Seat2String(DeclarerSeat())); } void aBridgeGame::HandleBidFinished() { m_is_bid = TRUE; // Done bidding!!! m_is_doubled = m_is_redoubled = FALSE; // last non-pass bid (or a pass if it was passed around) int which_bid = m_num_bids - 3; if (!m_bid_values[which_bid]) { // passed around!!! m_dealer_seat = RotateSeat(m_dealer_seat); NewHand(); m_bid_str = "Passed Out"; } else { while (m_bid_values[which_bid]<0) { if (m_bid_values[which_bid]==-2) { m_is_redoubled = TRUE; which_bid--; while (!m_bid_values[which_bid]) which_bid--; } if (m_bid_values[which_bid]==-1) { m_is_doubled = TRUE; which_bid--; while (!m_bid_values[which_bid]) which_bid--; } } // got the final bid... m_trump = m_bid_suits[which_bid]; m_bid_str = ""; m_bid_str << (int)m_bid_values[which_bid] << " " << Suit2String(m_trump); if (m_is_doubled) m_bid_str << " X"; if (m_is_redoubled) m_bid_str << "X"; while (which_bid >= 0) { if (m_bid_values[which_bid] > 0 && m_bid_suits[which_bid]==m_trump) { m_whose_lead = (Seat)((m_dealer_seat + which_bid)%4); } which_bid -= 2; } m_bid_str << " by " << Seat2String(m_whose_lead); m_dummy_seat = AcrossFrom(m_whose_lead); // actually across from declarer. m_whose_lead = RotateSeat(m_whose_lead); // leader is rotated from declarer. m_whose_turn = m_whose_lead; } m_bid_text->SetLabel(m_bid_str); if (m_played_by_me[DeclarerSeat()]) { SendEvent("IMDECLARER",""); DebugMsg("I am declarer"); } } /* Called when the main frame is closed */ bool aBridgeGame::OnCloseGame() { if (wxMessageBox("Are you sure you want to\nabandon the current game?", "Warning", wxYES_NO | wxICON_QUESTION, this) == wxNO) { return FALSE; } return TRUE; } bool aBridgeGame::IsLegal(const Card &card) { // Absolutely first check if the card is in the hand whose turn it is... if (! m_hands[m_whose_turn]->HasCard(card)) return false; // Should first check that it's played by me (in game.cpp) if (m_whose_turn == m_dummy_seat) { if (!m_played_by_me[DeclarerSeat()]) return FALSE; } else if (!m_played_by_me[m_whose_turn]) return FALSE; if (m_whose_lead == m_whose_turn) return true; // You can lead any card... // //printf("getting the suit that was led...\n"); Suit ledsuit = m_played[m_whose_lead]->GetSuit(); if (ledsuit == card.GetSuit()) return TRUE; if (!m_hands[m_whose_turn]->HasSuit(ledsuit)) return TRUE; return FALSE; } void aBridgeGame::HandlePlay(const wxString& who, const wxString& what) { wxStringTokenizer tokens(what, " "); Seat seat = String2Seat(tokens.GetNextToken()); if (seat != m_whose_turn) { DebugMsg("Error: someone trid to play out of turn!\n"); return; } Card card(tokens.GetNextToken(),faceup); DoPlayCard(seat, card); } void aBridgeGame::Claim() { wxString claim_lost = "0"; SendEvent("CLAIM",claim_lost); } void aBridgeGame::HandleClaim(const wxString& who, const wxString& what) { DebugMsg("Handling claim..."); long claim_value; what.ToLong(&claim_value); int tricks_lost_so_far; if (IsNS(m_dummy_seat)) { // EW tricks_lost_so_far = m_ew_tricks; } else { // NS tricks_lost_so_far = m_ns_tricks++; } long how_many_more_lost = (7 - tricks_lost_so_far) - claim_value; wxString claimtext = "Declarer claims to make all but "; claimtext << how_many_more_lost << " of the remaining tricks.\n"; claimtext << "Do you accept?"; int answer = wxMessageBox(claimtext, "Claim", wxYES_NO, GetParent()); if (answer == wxYES) { SendEvent("ACCEPTCLAIM",what); } else { SendEvent("REJECTCLAIM",what); } } void aBridgeGame::HandleClaimAccept(const wxString& what) { long claim_value; what.ToLong(&claim_value); if (IsNS(m_dummy_seat)) { m_ns_tricks = claim_value + 6; m_ew_tricks = 13 - m_ns_tricks; } else { m_ew_tricks = claim_value + 6; m_ns_tricks = 13 - m_ew_tricks; } HandDone(); } void aBridgeGame::OnMouseEvent(wxMouseEvent& event) { int mouseX = (int)event.GetX(); int mouseY = (int)event.GetY(); wxClientDC dc(this); PrepareDC(dc); dc.SetFont(* m_font); if (event.LeftDClick()) { LButtonDblClk(dc, mouseX, mouseY); } else if (event.LeftDown()) { LButtonDown(dc, mouseX, mouseY); } } // Called when the left button is double clicked void aBridgeGame::LButtonDblClk(wxDC& dc, int x, int y) { } void aBridgeGame::CardDealt(const wxString &str) { wxStringTokenizer my_cards(str, " "); while ( my_cards.HasMoreTokens() ) { wxString cardname = my_cards.GetNextToken(); wxString seat_str = my_cards.GetNextToken(); Seat seat = String2Seat(seat_str); // sometimes refuse to show cards (if it doesn't look reasonable) if (m_played_by_me[seat] || seat == m_dummy_seat || seat == DeclarerSeat()) { Card card(cardname, faceup); if (!m_hand_visible[seat] || m_hands[seat]->GetNumCards() == 13) { // if it's not already visible, make it visible and reset the pile. m_hands[seat]->ResetHand(); m_hand_visible[seat] = true; } m_hands[seat]->AddCard(card); m_hands[seat]->TurnCards(faceup); m_hand_known[seat] = true; } else { DebugMsg("I'm refusing to show cards for seat " + seat_str); } } Refresh(); } // Left button is pressed void aBridgeGame::ProcessGameEvent(GameEvent &evt) { wxString type = evt.GetType(); wxString msg = evt.GetMyMessage(); if (type.IsSameAs("CARDCLICKED")) { if (!m_is_bid) return; // We're not done bidding! Card card(msg); if (IsLegal(card)) { SendEvent("PLAY", Seat2String(m_whose_turn) + " " + msg); DoPlayCard(m_whose_turn, card); } } else { evt.Skip(); } } void aBridgeGame::LButtonDown(wxDC& dc, int x, int y) { Card card; if (!m_is_bid) return; // We're not done bidding! /*if (m_hands[m_whose_turn]->IsThereCard(x,y)) { card = m_hands[m_whose_turn]->GetCard(x,y); if (IsLegal(card)) { SendEvent("PLAY", Seat2String(m_whose_turn) + " " + card.GetString()); DoPlayCard(m_whose_turn, card); } }*/ } Seat aBridgeGame::TrickWinner() { Card played[4]; int i; for (i=0;i<4;i++) played[i].SetSameAs(m_played[i]->GetCard()); int winner = 4; int rank = 0; for (i=0;i<4;i++) { if (played[i].GetSuit() == m_trump && played[i].GetBridgeValue() > rank) { winner = i; rank = played[i].GetBridgeValue(); } } if (winner < 4) { // //printf("The winner is %s!\n", Seat2String((Seat)winner).c_str); return (Seat) winner; } Suit ledsuit = played[m_whose_lead].GetSuit(); for (i=0;i<4;i++) { if (played[i].GetSuit() == ledsuit && played[i].GetBridgeValue() > rank) { winner = i; rank = played[i].GetBridgeValue(); } } // //printf("The winner is %s!\n", Seat2String((Seat)winner).c_str); return (Seat) winner; } void aBridgeGame::DoPlayCard(Seat seat, Card card) { if (m_whose_turn == m_whose_lead) { // hide previous trick... int i; for(i=0;i<4;i++) { m_played[i]->Hide(); m_played[i]->Refresh(); } SendEvent("LASTTRICK", m_played[SOUTH]->GetString() +":"+ m_played[NORTH]->GetString() +":"+ m_played[EAST]->GetString() +":"+ m_played[WEST]->GetString()); } m_player[m_whose_turn]->YourTurn(FALSE); m_whose_turn = RotateSeat(m_whose_turn); m_played[seat]->SetSameAs(card); m_played[seat]->TurnCard(faceup); m_played[seat]->Show(); m_hands[seat]->PlayCard(card); m_played_number++; if (m_whose_turn == m_whose_lead) { // The trick is finished... m_whose_turn = m_whose_lead = TrickWinner(); DebugMsg("The trick was won by "+Seat2String(m_whose_lead)); if (IsNS(m_whose_lead)) { // EW m_ns_tricks++; } else { // NS m_ew_tricks++; } } if (m_played_number == 52) { HandDone(); } m_played[seat]->Refresh(); m_hands[seat]->Refresh(); if (m_whose_turn == m_whose_lead) { wxString temp("East West: "); temp << m_ew_tricks; m_ew_trick_text->SetLabel(temp); wxString temp2("North South: "); temp2 << m_ns_tricks; m_ns_trick_text->SetLabel(temp2); } m_player[m_whose_turn]->YourTurn(TRUE); } void aBridgeGame::DealDone() { for (int i=0;i<4;i++) { m_hands[i]->Empty(); while (m_hands[i]->GetNumCards() < 13) { Card card(1, facedown); m_hands[i]->AddCard(card); } m_hands[i]->Refresh(); m_player[i]->YourTurn(false); } m_is_dealt = TRUE; m_ns_tricks = m_ew_tricks = 0; m_whose_turn = m_dealer_seat; m_player[m_whose_turn]->YourTurn(TRUE); if (m_played_by_me[m_whose_turn]) { m_bid_window = new BidWindow(this); m_bid_window->DisallowOneMove(); m_bid_window->Show(TRUE); } } void aBridgeGame::HandDone() { SendEvent("LASTTRICK", m_played[SOUTH]->GetString() +":"+ m_played[NORTH]->GetString() +":"+ m_played[EAST]->GetString() +":"+ m_played[WEST]->GetString()); if (m_whose_turn == m_whose_lead) { wxString temp("East West: "); temp << m_ew_tricks; m_ew_trick_text->SetLabel(temp); wxString temp2("North South: "); temp2 << m_ns_tricks; m_ns_trick_text->SetLabel(temp2); } if (m_scorer->HandleHand(m_dummy_seat,m_current_bid_val, m_current_bid_suit, m_is_doubled, m_is_redoubled, m_ns_tricks)) { // rubber finished, should open window. MatchWin *hello = new MatchWin(this, m_scorer, m_names); hello->Show(true); m_scorer->NewMatch(); } m_dealer_seat = RotateSeat(m_dealer_seat); NewHand(); } void aBridgeGame::NewHand() { for (int i=0;i<4;i++) { m_hands[i]->ResetHand(); m_hand_visible[i] = m_played_by_me[i]; m_hand_known[i] = false; // hand is only known when it is dealt m_played[i]->Hide(); } DebugMsg("Dealer is " + Seat2String(m_dealer_seat)); m_played_number = 0; m_whose_turn = m_dealer_seat; m_is_bid = FALSE; m_is_dealt = FALSE; m_ns_tricks = m_ew_tricks = 0; m_num_bids = 0; m_current_bid_val = 0; m_current_bid_suit = notrump; m_current_bid_winner = m_dealer_seat; m_bid_str = ""; m_trump = notrump; for (int i=0;i<4;i++) m_player[i]->BlankBid(); SendEvent("NEWHAND",""); m_bid_text->SetLabel(""); } void aBridgeGame::NewRubber() { NewHand(); m_dealer_seat = m_whose_turn = SOUTH; m_scorer->NewMatch(); DebugMsg("Playing new rubber!!!"); for (int i=0;i<4;i++) { m_player[i]->YourTurn(false); m_hands[i]->ResetHand(); } Refresh(); } void aBridgeGame::SendEvent(const wxString &type, const wxString &msg) { GameEvent my_event( this, type, msg); wxPostEvent(GetParent()->GetEventHandler(), my_event); //if ( GetParent()->GetEventHandler()->ProcessEvent( my_event ) == FALSE ) // wxLogWarning( "game: Could not process sent event!" ); } aBridgeScoreBase *aBridgeGame::GetScores() { return m_scorer; } void aBridgeGame::TableMoved() { if (!m_is_bid && m_is_dealt && m_played_by_me[m_whose_turn]) { // There should be a bid window... m_bid_window->ParentMoved(); } } void aBridgeGame::SetDealerSeat(Seat s) { m_dealer_seat = s; }