1 /* flatdice --- the dice that remember their last value
2  * by Oohara Yuuma <oohara@libra.interq.or.jp>
3  * $Id: flatdice.c,v 1.20 2005/07/06 13:26:10 oohara Exp $
4  */
5 
6 #include <stdio.h>
7 /* malloc, rand  */
8 #include <stdlib.h>
9 
10 #include "flatdice.h"
11 
12 /* return a random integer in [0, side - 1]
13  * the return value is adjusted so that
14  * * it is likely to differ from the last value
15  *   (this adjustment is strong if "repeat" is large)
16  * * all possible values appear almost same times
17  *   (this adjustment is weak if "randomness" is large)
18  */
19 flatdice *
flatdice_new(int side,int randomness,int repeat)20 flatdice_new(int side, int randomness, int repeat)
21 {
22   int i;
23   flatdice *new = NULL;
24 
25   /* sanity check */
26   if (side <= 0)
27   {
28     fprintf(stderr, "flatdice_new: side is non-positive (%d)\n", side);
29     return NULL;
30   }
31   if (randomness <= 0)
32   {
33     fprintf(stderr, "flatdice_new: randomness is non-positive (%d)\n",
34             randomness);
35     return NULL;
36   }
37   if (repeat <= 0)
38   {
39     fprintf(stderr, "flatdice_new: repeat is non-positive (%d)\n", repeat);
40     return NULL;
41   }
42 
43   new = (flatdice *) malloc(sizeof(flatdice));
44   if (new == NULL)
45   {
46     fprintf(stderr, "flatdice_new: malloc(new) failed\n");
47     return NULL;
48   }
49 
50   new->side = side;
51   new->randomness = randomness;
52   new->repeat = repeat;
53   new->stock = NULL;
54   new->last = -1;
55 
56   new->stock = (int *) malloc(sizeof(int) * side);
57   if (new == NULL)
58   {
59     fprintf(stderr, "flatdice_new: malloc(new->stock) failed\n");
60     flatdice_delete(new);
61     return NULL;
62   }
63   for (i = 0; i < side; i++)
64     new->stock[i] = randomness;
65 
66   return new;
67 }
68 
69 void
flatdice_delete(flatdice * p)70 flatdice_delete(flatdice *p)
71 {
72   if (p != NULL)
73   {
74     if (p->stock != NULL)
75     {
76       free(p->stock);
77       p->stock = NULL;
78     }
79     free(p);
80   }
81 }
82 
83 /* return 1 (true) or 0 (false) */
84 int
flatdice_valid(flatdice * p,int quiet)85 flatdice_valid(flatdice *p, int quiet)
86 {
87   int i;
88   int total;
89 
90   /* sanity check */
91   if (p == NULL)
92   {
93     if (!quiet)
94       fprintf(stderr, "flatdice_valid: p is NULL\n");
95     return 0;
96   }
97   if (p->side <= 0)
98   {
99     if (!quiet)
100       fprintf(stderr, "flatdice_valid: p->side is non-positive (%d)\n",
101               p->side);
102     return 0;
103   }
104   if (p->randomness <= 0)
105   {
106     if (!quiet)
107       fprintf(stderr, "flatdice_valid: p->randomness is non-positive (%d)\n",
108               p->randomness);
109     return 0;
110   }
111   if (p->repeat <= 0)
112   {
113     if (!quiet)
114       fprintf(stderr, "flatdice_valid: p->repeat is non-positive (%d)\n",
115               p->repeat);
116     return 0;
117   }
118   if (p->stock == NULL)
119   {
120     if (!quiet)
121       fprintf(stderr, "flatdice_valid: p->stock is NULL\n");
122     return 0;
123   }
124 
125   total = 0;
126   for (i = 0; i < p->side; i++)
127   {
128     if (p->stock[i] < 0)
129     {
130       if (!quiet)
131         fprintf(stderr, "flatdice_valid: p->stock[%d] is negative (%d)\n",
132                 i, p->stock[i]);
133       return 0;
134     }
135     if (i == p->last)
136       total += p->stock[i];
137     else
138       total += p->stock[i] * p->repeat;
139   }
140   if (total <= 0)
141   {
142     if (!quiet)
143       fprintf(stderr, "flatdice_valid: total is non-positive (%d)\n", total);
144     return 0;
145   }
146 
147   return 1;
148 }
149 
150 /* return [0, p->side - 1] on success, -1 on error */
151 int
flatdice_next(flatdice * p)152 flatdice_next(flatdice *p)
153 {
154   int i;
155   int total;
156   int n1;
157   int n2;
158   int needed;
159   int result = -1;
160 
161   /* sanity check */
162   if (p == NULL)
163   {
164     fprintf(stderr, "flatdice_next: p is NULL\n");
165     return -1;
166   }
167   if (!flatdice_valid(p, 0))
168   {
169     fprintf(stderr, "flatdice_next: p is invalid\n");
170     return -1;
171   }
172 
173   total = 0;
174   for (i = 0; i < p->side; i++)
175   {
176     if (i == p->last)
177       total += p->stock[i];
178     else
179       total += p->stock[i] * p->repeat;
180   }
181 
182   n1 = rand() % total + 1;
183   n2 = 0;
184   for (i = 0; i < p->side; i++)
185   {
186     if (((i == p->last) && (n2 + p->stock[i] >= n1))
187         || ((i != p->last) && (n2 + p->stock[i] * p->repeat >= n1)))
188     {
189       result = i;
190       p->last = i;
191       (p->stock[i])--;
192       if (p->stock[i] < 0)
193         p->stock[i] = 0;
194       break;
195     }
196     if (i == p->last)
197       n2 += p->stock[i];
198     else
199       n2 += p->stock[i] * p->repeat;
200   }
201 
202   needed = p->randomness;
203   for (i = 0; i < p->side; i++)
204   {
205     if (needed > p->randomness - p->stock[i])
206       needed = p->randomness - p->stock[i];
207   }
208   if (needed < 0)
209     needed = 0;
210   for (i = 0; i < p->side; i++)
211     p->stock[i] += needed;
212 
213   return result;
214 }
215 
216 /* return 0 on success, 1 on error */
217 int
flatdice_reset(flatdice * p)218 flatdice_reset(flatdice *p)
219 {
220   int i;
221 
222   /* sanity check */
223   if (p == NULL)
224   {
225     fprintf(stderr, "flatdice_reset: p is NULL\n");
226     return 1;
227   }
228   if (!flatdice_valid(p, 0))
229   {
230     fprintf(stderr, "flatdice_reset: p is invalid\n");
231     return 1;
232   }
233 
234   for (i = 0; i < p->side; i++)
235     p->stock[i] = p->randomness;
236   p->last = -1;
237 
238   return 0;
239 }
240