1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
|
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
enum parser_state_e {
ST_NUMBER_BEFORE_DICE = 0,
ST_NUMBER_AFTER_DICE = 1,
};
struct state_s {
enum parser_state_e parser_state;
unsigned number_before_dice;
unsigned number_after_dice;
unsigned result;
int eof;
int syntax_error;
};
void print_usage(char* argv0)
{
printf("usage: %s <command>\n", argv0);
puts(" examples: d20, 2d8+4, 1d6+d8+5");
}
unsigned diceroll(unsigned dice, unsigned rolls)
{
unsigned result = 0;
for (; rolls > 0; rolls--) {
result += 1 + rand() % dice;
}
return result;
}
struct state_s eval_step(struct state_s state, char c)
{
switch (c) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
/* anything numeric */
if (state.parser_state == ST_NUMBER_BEFORE_DICE) {
state.number_before_dice *= 10;
state.number_before_dice += c - '0';
} else if (state.parser_state == ST_NUMBER_AFTER_DICE) {
state.number_after_dice *= 10;
state.number_after_dice += c - '0';
}
break;
case 'd':
case 'D':
/* dice indicator */
if (state.parser_state == ST_NUMBER_BEFORE_DICE) {
if (state.number_before_dice == 0) {
/* allow shorthand, treat e.g. "d20" as "1d20" */
state.number_before_dice = 1;
}
state.parser_state = ST_NUMBER_AFTER_DICE;
} else {
/* not in the format ABdXY */
state.syntax_error = 1;
}
break;
case '+':
case '\0':
/* end of input or just end of an element */
if (state.number_after_dice > 0) {
state.result +=
diceroll(state.number_after_dice, state.number_before_dice);
} else if (state.parser_state == ST_NUMBER_BEFORE_DICE) {
/* we don't have a number after the dice */
/* in this case we just have a "naked" value that we take as-is */
state.result += state.number_before_dice;
} else {
/* missing number after dice indicator */
state.syntax_error = 1;
break;
}
state.number_before_dice = 0;
state.number_after_dice = 0;
state.parser_state = ST_NUMBER_BEFORE_DICE;
if (c == '\0') {
state.eof = 1;
}
break;
default:
state.syntax_error = 1;
break;
}
return state;
}
int eval(char* c)
{
struct state_s state = {0};
while (1) {
state = eval_step(state, *c);
if (state.syntax_error) {
puts("syntax error");
return 1;
}
if (state.eof) {
break;
}
c++;
}
printf("%d\n", state.result);
return 0;
}
/**
* platform dependent way to generate a good seed for the random number
* generator
*/
unsigned get_seed()
{
unsigned seed;
#ifdef __linux__
FILE* fp = fopen("/dev/urandom", "rb");
fread(&seed, sizeof(unsigned), 1, fp);
fclose(fp);
#else
seed = time(NULL);
#endif
return seed;
}
int main(int argc, char* argv[])
{
if (argc < 2) {
print_usage(argv[0]);
return 1;
}
srand(get_seed());
return eval(argv[1]);
}
|