// This is a translation into C of the Visual Basic code at // http://groups.yahoo.com/group/wordgame-programmers/message/183 // originally written by someone who would prefer to remain anonymous // and translated by Graham Toal #include <stdio.h> #include <string.h> #include <assert.h> #include <values.h> /* For MAXLONG */ static int Debug_Probs=FALSE; static int PutCount; static int WantedCount; static int BagCount; static int RackCounts[256]; static int BagCountError; static int BagCounts[256]; static int RackTiles; static int WildCardTiles; static int ResultRow; static int PlacementCountsMin[120]; static int PlacementCountsMax[120]; static int Placements; static double nCr (int n, int r) { static double memo[101][8]; static int initialised = FALSE; double _nCr; int i, rr; assert(n <= 100); assert(r <= 7); if (!initialised) { int nn, rr; for (rr = 0; rr <= 7; rr++) { for (nn = 0; nn <= 100; nn++) { memo[nn][rr] = (double) -1.0; } } } if (!(memo[n][r] < 0)) return memo[n][r]; /* I expect storing precomputed values from nCr(0,0) up to nCr(100,7) */ /* would be faster: a task for later */ if (r > n) return 0.0; _nCr = 1.0; if ((r + r) <= n) { rr = r; } else { rr = n - r; } i = 1; for (;;) { if (i > rr) break; _nCr = _nCr * (double)n / (double)r; n = n - 1; r = r - 1; rr = r; } return (memo[n][r] = _nCr); } static double DoPlacements (int Tile) { int n; double p; p = 0.0; /* do from minimum wanted count up to the maximum in the bag */ n = PlacementCountsMin[Tile]; while (n <= PlacementCountsMax[Tile]) { /* update the count of tiles in the rack */ PutCount = PutCount + n; if (PutCount <= RackTiles) { if (Tile < Placements) { /* recursively place the next tile(s) */ p = p + nCr (PlacementCountsMax[Tile], n) * DoPlacements (Tile + 1); } else { assert (PutCount >= WantedCount); /* the last wanted tile and the wildcard tiles */ p = p + nCr (PlacementCountsMax[Tile], n) * nCr (WildCardTiles, RackTiles - PutCount); } PutCount = PutCount - n; } else { /* Too many tiles now so we exit early */ PutCount = PutCount - n; break; } n += 1; } return p; } static double Prob (void) { int n; assert (WantedCount <= RackTiles); PutCount = 0; Placements = 0; /* for (n = {blank, alltiles} ... could optimise */ for (n = 0; n < 256; n++) { if (RackCounts[n] > 0) { Placements = Placements + 1; PlacementCountsMin[Placements] = RackCounts[n]; PlacementCountsMax[Placements] = BagCounts[n]; } } assert (Placements <= RackTiles); return DoPlacements (1); } static void GetCounts (char *Bag, char *Tiles) { int n, x; BagCount = strlen (Bag); WantedCount = strlen (Tiles); WildCardTiles = BagCount; for (n = 0; n < 256; n++) { BagCounts[n] = 0; RackCounts[n] = 0; } for (n = 0; n < strlen (Bag); n++) { x = Bag[n]; BagCounts[x] = BagCounts[x] + 1; } for (n = 0; n < strlen (Tiles); n++) { x = Tiles[n]; RackCounts[x] = RackCounts[x] + 1; if (RackCounts[x] == 1) { /* exit if the wanted tile does not exist in the bag */ if (BagCounts[x] == 0) { BagCountError = TRUE; fprintf(stderr, "BagCountError: %c - GetCounts(%s,%s)\n", x, Bag, Tiles); assert(BagCounts[x]); return; } /* any tiles we want must be removed from the selection of wildcard tiles */ WildCardTiles = WildCardTiles - BagCounts[x]; } } } double RackProbAndCount (char *Tiles, char *Bag, int RackSize, double *Count, double *Total) { double _RackProb, RackProb2, max; if (Count != NULL) *Count = 0.0; if (Total != NULL) *Total = 0.0; BagCountError = FALSE; RackTiles = RackSize; GetCounts (Bag, Tiles); /* error if any wanted tile in "Tiles" does not exist in "Bag" */ if (BagCountError) { fprintf(stderr, "*** BagCountError\n"); return 0.0; } if (RackTiles > BagCount) { fprintf(stderr, "*** RackTiles > BagCount\n"); return 0.0; } _RackProb = Prob (); RackProb2 = _RackProb / (max = nCr (BagCount, RackTiles)); if (Debug_Probs) printf("%s %s %d %1.10f (%0.0f ways out of %0.0f)\n", Tiles, Bag, RackSize, RackProb2, _RackProb, max); if (Count != NULL) *Count = _RackProb; if (Total != NULL) *Total = max; return (RackProb2); } double RackProb(char *Tiles, char *Bag, int RackSize) { return RackProbAndCount (Tiles, Bag, RackSize, NULL, NULL); } double RackCount(char *Tiles, char *Bag, int RackSize) /* error if any wanted tile in "Tiles" does not exist in "Bag" */ { double Count; (void)RackProbAndCount (Tiles, Bag, RackSize, &Count, NULL); return Count; }