// ------- BBallCpW.Cpp Baseball probability program in C++
/* =========================================================== *\
** BBallCpW.cpp **
** =========================================================== **
** **
** =========================================================== **
** Copyright (c) 1994 by Harry J. Smith. All rights reserved. **
\* =========================================================== */
char name[] = "BBallCpW - Baseball probability.";
char version[] = "C++ Windows Version 2.04, last revised: 1994-07-08, 0600 hour";
char author[] = "Copyright \xA9 1981-1994 by author: Harry J. Smith,";
char address[] = "19628 Via Monte Dr., Saratoga CA 95070. All rights reserved.";
char hint1[] = "Uses a bivariate binomial distribution as a model,";
char hint2[] = "and assumes each game is a 50-50 chance.";
// Computes the probability that the 1st place team will beat the 2nd place
// team for the division title, assuming each has a 50-50 chance of winning
// any given future game. Uses a bivariate binomial distribution as a model.
// Developed in Turbo Pascal 5.0, converted to
// Converted to Borland C++ 4.0 and OWL 2.0
#include <owl\applicat.h>
#include <owl\framewin.h>
#include <owl\menu.h>
#include <owl\dialog.h>
#include <owl\edit.h>
#include <owl\validate.h>
#include <owl\printer.h>
#include <classlib\date.h>
#include <mem.h>
#include <cstring.h>
#pragma hdrstop
#include "BBallCpW.rh"
#include <stdio.h> // Definitions for stream input/output
#include <math.h> // Definitions for the math floating point package
#include <conio.h> // Direct MS-DOS console input/output, getch()
#include <stdlib.h> // Definition of exit() etc.
#include <string.h> // Definition of srtcpy() etc.
// Detect Windows NT or Win32s for 32-bit mode
#ifdef __WIN32__
char WinMode[] = ", (32-bit mode)";
#else
char WinMode[] = ", (16-bit mode)";
#endif
#define GL1_SIZE 4
#define GL2_SIZE 4
#define GE_SIZE 3
#define GA_SIZE 6
#define TN1_SIZE 13
#define TN2_SIZE 13
#define DATE_SIZE 13
#define P_SIZE 16
#define Q_SIZE 16
#define OOQ_SIZE 16
#define POQ_SIZE 16
#define MNT1_SIZE 16
#define MNW1_SIZE 16
#define MNT2_SIZE 16
#define MNW2_SIZE 16
#define ER_GA_SIZE 16
#define ER_GE_SIZE 16
struct TBBBuffer {
char GL1[GL1_SIZE];
char GL2[GL2_SIZE];
char GE[GE_SIZE];
char GA[GA_SIZE];
char TN1[TN1_SIZE];
char TN2[TN2_SIZE];
char Date[DATE_SIZE];
};
// Global constants and variables
const char ESC = 27;
char TN1[11]; // Team's name, 1st place team
char TN2[11]; // Team's name, 2nd place team
int GL1; // Games Left to play, 1st place team
int GL2; // Games Left to play, 2nd place team
int GE; // Games to play each other
double GA; // Games 1st place team is ahead. 0, 0.5, ...
long GA2; // Twice games ahead = 2 * GA, 0, 1, ...
long MNT1; // Magic Number to tie for 1st place team
long MNW1; // Magic Number to win for 1st place team
long MNT2; // Magic Number to tie for 2nd place team
long MNW2; // Magic Number to win for 2nd place team
double P; // Probability that 1st place team beats 2nd place team
double Q; // Probability that 2nd place team beats 1st place team
BOOL GAEr; // True if GA value not possible
BOOL GEEr; // True if GE value not possible
BOOL PEr; // True if Overflow while computing P
string msg; // Message for printer
// Procedure prototypes
void ExpandCase( void); // Compute related data
void ComputeProb( void); // Compute probability using a bivariate binomial
// distribution as a model
// ------- Compute related data
void ExpandCase( void)
{
long I;
GA2 = 2 * GA + 0.5; // Round
I = GL1 + GL2 - GA2;
MNT1 = I / 2; MNW1 = MNT1 + 1;
MNT2 = GL1 + GL2 - MNT1; MNW2 = MNT2 + 1;
I = GL1 + GL2 + GA2;
GAEr = (I % 2 == 1); // True if I is odd
} // --end-- ExpandCase
// ------- Compute probability using a bivariate binomial dist. as a model
void ComputeProb( void)
{
int I, J;
double A, B;
double* E; // 25 Binomial coefficients, games to play each other
double* S; // 25 Running sum of 2 * E[I]
double* F; // 163 B. C., games not played with each other
double* G; // 163 Sum of 2 * E[I] for 2nd place team win, E[I] for tie
E =(double*) calloc( GE+1, sizeof(double));
S =(double*) calloc( GE+1, sizeof(double));
F =(double*) calloc( (size_t)max(1L, MNT1+1), sizeof(double));
G =(double*) calloc( (size_t)max(1L, MNT1+1), sizeof(double));
PEr = FALSE;
A = GL1 + GL2 - GE - GE; // A = not played with each other games
B = 1.0;
F[0] = 1.0;
for (I = 1; I <= MNT1; I++) { // Compute binomial coefficients
F[I] = F[I - 1] * A / B;
A = A - 1.0;
B = B + 1.0;
}
A = GE;
B = 1.0;
E[0] = 1.0;
S[0] = 2.0;
for (I = 1; I <= GE; I++) { // Compute binomial coefficients
E[I] = E[I - 1] * A / B;
A = A - 1.0;
B = B + 1.0;
S[I] = S[I - 1] + 2 * E[I];
}
for (I = 0; I <= MNT1; I++) { // Compute G[I]
J = (int)((MNT1 - I) / 2);
if (J <= GE) {
G[I] = S[J];
if ((J + J) == (MNT1 - I)) G[I] = G[I] - E[J]; // Adjust for tie
}
else
G[I] = S[GE];
}
Q = 0.0; // Compute probability that 2nd place team beats 1st place team
for (I = 0; I <= MNT1; I++) {
Q = Q + F[I] * G[I];
}
A = GL1 + GL2 - GE + 1;
B = pow( 2.0, A); // 2 ** Flips
Q = Q / B;
P = 1.0 - Q;
//catch (...) {
// string msg;
// string NL('\n'), DL("\n\n");
// msg += """";
// msg += "Overflow" + DL;
// msg += "Input numbers are too large!";
// MessageBox( // Display message
// NULL,
// msg.c_str(),
// "Overflow",
// MB_OK);
// P = Q = 0; PEr = TRUE;
//}
} // --end-- ComputeProb
// ===========================================================
// The Printout class
// ===========================================================
class TBBPrintout: public TPrintout {
public:
TBBPrintout(TWindow* window, const char* title);
protected:
void PrintPage(int page, TRect& rect, unsigned flags);
private:
TWindow* parentWindow;
};
// Constructor
TBBPrintout::TBBPrintout(
TWindow* window, const char* title)
: TPrintout(title)
{
parentWindow = window;
}
// Print pages of document (only 1 page)
void
TBBPrintout::PrintPage(
int /*page*/, TRect& rect, unsigned /*flags*/)
{
TPrintDC& dc = *DC;
TSize pageSize = PageSize;
TEXTMETRIC tm;
int h;
//
// Size the fonts to whatever DC we print to
//
h = -MulDiv(dc.GetDeviceCaps(LOGPIXELSY), abs(-12), 72); // 12-point font
TFont fontNormal(h, 0, 0, 0, FW_NORMAL, 0, 0, 0,
ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DRAFT_QUALITY, VARIABLE_PITCH | FF_SWISS, NULL);
TSize extent;
dc.SetBkMode(TRANSPARENT);
dc.SelectObject(fontNormal);
dc.GetTextMetrics(tm);
dc.DrawText(msg.c_str(), -1, rect, DT_LEFT);
parentWindow->Paint(*DC, FALSE, rect);
}
// ===========================================================
// The application's main dialog
// ===========================================================
class TBBDialog: public TDialog {
public:
TBBDialog(TWindow* parent, TBBBuffer* BBBuffer);
~TBBDialog();
TEdit* EditDate;
protected:
void CmCheckInput(); // Respond to Check Input button
void CmUpdateOutput(); // Respond to Update Output button
void CmReset(); // Respond to Reset button
void CmFilePrintSetup(); // Respond to File|Print Setup menu command
void CmFilePrint(); // Respond to File|Print menu command
void CmFileExit(); // Respond to File|Exit menu command
void CmHelpTest(); // Respond to Help|Test menu command
void CmHelpAbout(); // Respond to Help|About menu command
void UpdateInput(); // Common code
void UpdateOutput(); // Common code
private:
TPrinter* printer;
TEdit* EditGL1;
TEdit* EditGL2;
TEdit* EditGE;
TEdit* EditGA;
TEdit* EditTN1;
TEdit* EditTN2;
//TEdit* EditDate;
TStatic* StaticP;
TStatic* StaticQ;
TStatic* Static1oQ;
TStatic* StaticPoQ;
TStatic* StaticMNT1;
TStatic* StaticMNW1;
TStatic* StaticMNT2;
TStatic* StaticMNW2;
TStatic* ErrorGE;
TStatic* ErrorGA;
DECLARE_RESPONSE_TABLE(TBBDialog);
};
DEFINE_RESPONSE_TABLE1(TBBDialog, TDialog)
EV_COMMAND(CM_CHECK_INPUT, CmCheckInput),
EV_COMMAND(CM_UPDATE_OUTPUT, CmUpdateOutput),
EV_COMMAND(CM_RESET, CmReset),
EV_COMMAND(CM_FILE_PRINTSETUP, CmFilePrintSetup),
EV_COMMAND(CM_FILE_PRINT, CmFilePrint),
EV_COMMAND(CM_FILE_EXIT, CmFileExit),
EV_COMMAND(CM_HELP_TEST, CmHelpTest),
EV_COMMAND(CM_HELP_ABOUT, CmHelpAbout),
END_RESPONSE_TABLE;
// Constructor
TBBDialog::TBBDialog(
TWindow* parent, TBBBuffer* BBBuffer)
: TDialog(parent, DIALOG_1)
{
EditGL1 = new TEdit(this, ED_GL1, GL1_SIZE);
EditGL1->SetValidator(new TRangeValidator(0, 162));
EditGL2 = new TEdit(this, ED_GL2, GL2_SIZE);
EditGL2->SetValidator(new TRangeValidator(0, 162));
EditGE = new TEdit(this, ED_GE, GE_SIZE);
EditGE->SetValidator(new TRangeValidator(0, 24));
EditGA = new TEdit(this, ED_GA, GA_SIZE);
EditGA->SetValidator(new TFilterValidator("0-9."));
EditTN1 = new TEdit(this, ED_TN1, TN1_SIZE);
EditTN2 = new TEdit(this, ED_TN2, TN2_SIZE);
EditDate = new TEdit(this, ED_DATE, DATE_SIZE);
SetTransferBuffer(BBBuffer);
StaticP = new TStatic(this, O_P, P_SIZE);
StaticQ = new TStatic(this, O_Q, Q_SIZE);
Static1oQ = new TStatic(this, O_1OQ, OOQ_SIZE);
StaticPoQ = new TStatic(this, O_POQ, POQ_SIZE);
StaticMNT1 = new TStatic(this, O_MNT1, MNT1_SIZE);
StaticMNW1 = new TStatic(this, O_MNW1, MNW1_SIZE);
StaticMNT2 = new TStatic(this, O_MNT2, MNT2_SIZE);
StaticMNW2 = new TStatic(this, O_MNW2, MNW2_SIZE);
ErrorGE = new TStatic(this, ER_GE, ER_GE_SIZE);
ErrorGA = new TStatic(this, ER_GA, ER_GA_SIZE);
printer = new TPrinter;
}
// Destructor
TBBDialog::~TBBDialog() {
delete printer;
}
// Respond to Check Input button
void
TBBDialog::CmCheckInput() {
UpdateInput();
}
// Respond to Update Output button
void
TBBDialog::CmUpdateOutput() {
UpdateOutput();
}
// Respond to Reset button
void
TBBDialog::CmReset() {
EditGL1->SetText("");
EditGL2->SetText("");
EditGE->SetText("");
EditGA->SetText("");
EditTN1->SetText("");
EditTN2->SetText("");
TDate Date;
string msg = Date.AsString();
EditDate->SetText(msg.c_str());
StaticP->SetText("");
StaticQ->SetText("");
Static1oQ->SetText("");
StaticPoQ->SetText("");
StaticMNT1->SetText("");
StaticMNW1->SetText("");
StaticMNT2->SetText("");
StaticMNW2->SetText("");
ErrorGE->SetText("");
ErrorGA->SetText("");
}
// Respond to File|Print Setup menu command
void
TBBDialog::CmFilePrintSetup()
{
printer->Setup(this);
}
// Respond to File|Print menu command
void
TBBDialog::CmFilePrint()
{
string NL("\n "), DL = ("\n") + NL;
msg = "\n\n" + DL;
msg += name + NL;
msg += version;
msg += WinMode + NL;
msg += author + NL;
msg += address + DL;
msg += hint1 + NL;
msg += hint2 + DL;
char st[16];
EditDate->GetText(st, DATE_SIZE);
msg += st;
msg += " = Date of morning after last game" + NL;
EditGL1->GetText(st, GL1_SIZE);
msg += st;
msg += " = Games Left to play, 1st place team (";
msg += TN1;
msg += ")" + NL;
EditGL2->GetText(st, GL2_SIZE);
msg += st;
msg += " = Games Left to play, 2nd place team (";
msg += TN2;
msg += ")" + NL;
EditGE->GetText(st, GE_SIZE);
msg += st;
msg += " = Games to play each other" + NL;
EditGA->GetText(st, GA_SIZE);
msg += st;
msg += " = Games 1st place team is ahead. 0, 0.5, ..." + DL;
StaticMNT1->GetText(st, MNT1_SIZE);
msg += st;
msg += " = Magic Number to tie for 1st place team" + NL;
StaticMNW1->GetText(st, MNW1_SIZE);
msg += st;
msg += " = Magic Number to win for 1st place team" + NL;
StaticMNT2->GetText(st, MNT2_SIZE);
msg += st;
msg += " = Magic Number to tie for 2nd place team" + NL;
StaticMNW2->GetText(st, MNW2_SIZE);
msg += st;
msg += " = Magic Number to win for 2nd place team" + DL;
StaticP->GetText(st, P_SIZE);
msg += st;
msg += " = Probability that 1st place team beats 2nd place team" + NL;
StaticQ->GetText(st, Q_SIZE);
msg += st;
msg += " = Probability that 2nd place team beats 2nd place team" + NL;
StaticQ->GetText(st, Q_SIZE);
msg += st;
msg += " = Probability that 2nd place team beats 2nd place team" + NL;
Static1oQ->GetText(st, OOQ_SIZE);
msg += st;
msg += " = 1 / Q, (Odds = ";
StaticPoQ->GetText(st, POQ_SIZE);
msg += st;
msg += " : 1)";
if (printer) {
TBBPrintout printout(this, "BB Printout");
printer->Print(this, printout, TRUE);
}
}
// Respond to File|Exit menu command
void
TBBDialog::CmFileExit()
{
// MainWindow->CmFileExit();
CmExit();
CloseWindow();
}
// Respond to Help|Test menu command
void
TBBDialog::CmHelpTest() {
EditGL1->SetText("6");
EditGL2->SetText("7");
EditGE->SetText("0");
EditGA->SetText("1.5");
EditTN1->SetText("Braves");
EditTN2->SetText("Giants");
EditDate->SetText("1993-09-27");
UpdateOutput();
}
// Respond to Help|About menu command
void
TBBDialog::CmHelpAbout() {
string msg;
string NL('\n'), DL("\n\n");
msg += name + DL;
msg += version;
msg += WinMode + DL;
msg += author + DL;
msg += address + DL + DL;
msg += hint1 + NL;
msg += hint2;
MessageBox(
msg.c_str(),
"About BBallCpW",
MB_OK);
}
// Respond to Update Input common code
void
TBBDialog::UpdateInput() {
char st[16];
EditGL1->GetText(st, GL1_SIZE); GL1 = atoi(st);
EditGL2->GetText(st, GL2_SIZE); GL2 = atoi(st);
EditGE->GetText(st, GE_SIZE); GE = atoi(st);
EditGA->GetText(st, GA_SIZE); GA = atof(st);
sprintf( st, "%1d", GL1); EditGL1->SetText(st);
sprintf( st, "%1d", GL2); EditGL2->SetText(st);
sprintf( st, "%1d", GE); EditGE->SetText(st);
GA2 = floor(2 * GA + 0.5); // Round
GA = GA2 / 2.0;
if (GA2 % 2 == 1)
sprintf( st, "%.1f", GA);
else
sprintf( st, "%.0f", GA);
EditGA->SetText(st);
EditTN1->GetText(TN1, TN1_SIZE);
EditTN2->GetText(TN2, TN2_SIZE);
if (GE > GL1 || GE > GL2) {
GEEr = TRUE;
ErrorGE->SetText("ERROR -->");
//ErrorGE->SetTextColor(4); // Red
//EditGE->SetTextColor(4);
} else {
GEEr = FALSE;
ErrorGE->SetText("");
//ErrorGE->SetTextColor(0); // Black
//EditGE->SetTextColor(0);
}
ExpandCase();
if (GAEr) {
ErrorGA->SetText("ERROR -->");
//ErrorGA->SetTextColor(4); // Red
//EditGA->SetTextColor(4);
} else {
ErrorGA->SetText("");
//ErrorGA->SetTextColor(0); // Black
//EditGA->SetTextColor(0);
}
}
// Respond to Update Output common code
void
TBBDialog::UpdateOutput() {
UpdateInput();
char st[16];
if (GEEr) {
PEr = TRUE;
Q = 0;
} else {
ComputeProb();
}
if (PEr) {
StaticP->SetText("");
StaticQ->SetText("");
Q = 0;
} else {
sprintf( st, "%#6.4f", P); StaticP->SetText(st);
sprintf( st, "%#6.4f", Q); StaticQ->SetText(st);
if (!Q) { // If Q is zero
StaticP->SetText("One");
StaticQ->SetText("Zero");
} else {
if (!strcmp(st, "0.0000")) { // If Q looks like zero
StaticQ->SetText("Very Small");
StaticP->SetText("Almost 1.0000");
}
}
}
if (Q) {
if (1/Q < 9999999999.9999) {
sprintf( st, "%#6.4f", 1/Q); Static1oQ->SetText(st);
sprintf( st, "%#6.4f", P/Q); StaticPoQ->SetText(st);
} else {
Static1oQ->SetText("> 10^10");
StaticPoQ->SetText("> 10^10");
}
sprintf( st, "%13ld", MNT1); StaticMNT1->SetText(st);
sprintf( st, "%13ld", MNW1); StaticMNW1->SetText(st);
sprintf( st, "%13ld", MNT2); StaticMNT2->SetText(st);
sprintf( st, "%13ld", MNW2); StaticMNW2->SetText(st);
} else {
Static1oQ->SetText("");
StaticPoQ->SetText("");
StaticMNT1->SetText("");
StaticMNW1->SetText("");
StaticMNT2->SetText("");
StaticMNW2->SetText("");
}
}
// ===========================================================
// The application's main window
// ===========================================================
class TBBallCpWWin: public TFrameWindow {
public:
TBBallCpWWin(TWindow* parent, const char far* title);
~TBBallCpWWin();
protected:
void SetupWindow();
void CmFilePrintSetup();
void CmFileExit();
void CmRunGo();
void CmHelpAbout();
private:
TMenu* windowMenu; // Pointer to window's menu
TBBBuffer BBBuffer; // Dialog transfer buffers
TPrinter* printer;
DECLARE_RESPONSE_TABLE(TBBallCpWWin);
};
DEFINE_RESPONSE_TABLE1(TBBallCpWWin, TFrameWindow)
EV_COMMAND(CM_FILE_PRINTSETUP, CmFilePrintSetup),
EV_COMMAND(CM_FILE_EXIT, CmFileExit),
EV_COMMAND(CM_RUN_GO, CmRunGo),
EV_COMMAND(CM_HELP_ABOUT, CmHelpAbout),
END_RESPONSE_TABLE;
// Constructor
TBBallCpWWin::TBBallCpWWin(TWindow* parent, const char far* title)
: TFrameWindow(parent, title),
TWindow(parent, title)
{
AssignMenu(ID_MENU);
printer = new TPrinter;
memset(&BBBuffer, 0, sizeof(BBBuffer));
strcpy(BBBuffer.GL1, "");
strcpy(BBBuffer.GL2, "");
strcpy(BBBuffer.GE, "");
strcpy(BBBuffer.GA, "");
strcpy(BBBuffer.TN1, "");
strcpy(BBBuffer.TN2, "");
TDate Date;
string msg = Date.AsString();
strcpy(BBBuffer.Date, msg.c_str());
}
// Destructor
TBBallCpWWin::~TBBallCpWWin()
{
delete windowMenu;
delete printer;
}
// Initialize window object
void
TBBallCpWWin::SetupWindow()
{
TFrameWindow::SetupWindow();
// Create menu object interface for this window's menu
windowMenu = new TMenu(HWindow);
}
// Respond to File|Print Setup menu command
void
TBBallCpWWin::CmFilePrintSetup()
{
printer->Setup(this);
}
// Respond to File|Exit menu command
void
TBBallCpWWin::CmFileExit()
{
CmExit();
}
// Respond to Run|Go menu command
void
TBBallCpWWin::CmRunGo()
{
// Input GL1 from the operator
TBBDialog* BBDialog = new TBBDialog(this, &BBBuffer);
// BBDialog->SetIcon(this, ID_ICON);
if (BBDialog->Execute() == IDOK) {}
}
// Respond to Help|About information menu command
void
TBBallCpWWin::CmHelpAbout()
{
string msg;
string NL('\n'), DL("\n\n");
msg += name + DL;
msg += version;
msg += WinMode + DL;
msg += author + DL;
msg += address + DL + DL;
msg += hint1 + NL;
msg += hint2;
MessageBox(
msg.c_str(),
"About BBallCpW",
MB_OK);
}
// ===========================================================
// The application class
// ===========================================================
class TBBallCpWApp: public TApplication {
public:
TBBallCpWApp(const char far* name)
: TApplication(name) {};
void InitMainWindow();
};
// Initialize the program's main window
void
TBBallCpWApp::InitMainWindow()
{
//EnableCtl3d(); // Did not work in Windows NT
EnableBWCC();
MainWindow = new TBBallCpWWin(0, "Baseball Probability");
MainWindow->SetIcon(this, ID_ICON);
}
#pragma argsused
// Main program
int
OwlMain(int argc, char* argv[])
{
TBBallCpWApp app("BBallCpW");
return app.Run();
}
// --end-- file BBallCpW.Cpp