UVa 174 - Strategy

contents

  1. 1. Problem
  2. 2. Input
  3. 3. Output
  4. 4. Sample input
  5. 5. Sample output
  6. 6. Solution

Problem

A well known psychology experiment involves people playing a game in which they can either trade with each other or attempt to cheat the other player. If both players TRADE then each gains one point. If one TRADEs and the other CHEATs then the TRADEr loses 2 points and the CHEATer wins 2. If both CHEAT then each loses 1 point.

There are a variety of different strategies for playing this game, although most people are either unable to find a winning strategy, or, having decided on a strategy, do not stick to it. Thus it is fairer to attempt to evaluate these strategies by simulation on a computer. Each strategy is simulated by an automaton. An automaton is characterised by a program incorporating the strategy, a memory for previous encounters and a count reflecting the score of that automaton. The count starts at zero and is altered according to the above rules after each encounter. The memory is able to determine what happened on up to the last two encounters with each other contender.

Write a program that will read in details of up to 10 different strategies, play each strategy against each other strategy 10 times and then print out the final scores. Strategies will be in the form of simple programs obeying the following grammar:

1
2
3
4
5
6
7
8
<program> ::= <statement>.
<statement> ::= <command> tex2html_wrap_inline45 <ifstat>
<ifstat> ::= IF <condition> THEN <statement> ELSE <statement>
<condition> ::= <cond> tex2html_wrap_inline45 <cond> <op> <condition>
<op> ::= AND tex2html_wrap_inline45 OR
<cond> ::= <memory> {= tex2html_wrap_inline45 #} {<command> tex2html_wrap_inline45 NULL}
<memory> ::= {MY tex2html_wrap_inline45 YOUR} LAST {1 tex2html_wrap_inline45 2}
<command> ::= TRADE tex2html_wrap_inline45 CHEAT

Note that LAST1 refers to the previous encounter between these two automata, LAST2 to the encounter before that and that MY' andYOUR’ have the obvious meanings. Spaces and line breaks may appear anywhere in the program and are for legibility only. The symbol #' meansis not equal to’. NULL indicates that an encounter has not ocurred. The following are valid programs:

CHEAT.
IF MY LAST1 = CHEAT THEN TRADE ELSE CHEAT.
IFYOURLAST2=NULLTHENTRADEELSEIFYOURLAST1=TRADETHENTRADE
ELSECHEAT.

Input

Input will consist of a series of programs. Each program will be no longer than 255 characters and may be split over several lines for convenience. There will be no more than 10 programs. The file will be terminated by a line containing only a single `#’.

Output

Output will consist of one line for each line of input. Each line will consist of the final score of the relevant program right justified in a field of width 3.

Sample input

1
2
3
4
5
CHEAT.
IF MY LAST1 = CHEAT THEN TRADE ELSE CHEAT.
IFYOURLAST2=NULLTHENTRADEELSEIFYOURLAST1=TRADETHENTRADE
ELSECHEAT.
#

Sample output

1
2
3
1
-12
-13

Solution

題目描述:

現在這邊有兩台機器人,玩類似 “吹牛” 的紙牌遊戲,欺騙成功者將獲得點數。

而每一台機器的策略也有所不同,並且能根據對方前幾次和自己前幾次的策略進行判斷,給予每一台機器的策略模式,請兩兩互打 10 回合,最後輸出每一台機器的得分概況。

題目解法:

這題不只有語法分析,還有語意分析。總之,遞迴分析是最主要的技巧。
對於 AND 和 OR 運算,測資中似乎沒有優先順序的情況,所以直接由左而右進行即可。

而在分析的時候,嘗試將指針一直往後移動,並且解析出第一個符合條件的 token。

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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
#include <stdio.h>
#include <algorithm>
#include <vector>
#include <math.h>
#include <iostream>
#include <map>
#include <assert.h>
#include <string.h>
#include <ctype.h>
using namespace std;
enum ACTION {TRADE, CHEAT, EMPTY};
enum RECORD {LAST1, LAST2};
class Memory {
public:
map<RECORD, ACTION> R;
Memory() {
R[LAST1] = EMPTY;
R[LAST2] = EMPTY;
}
static ACTION getAction(string s) {
if(s == "TRADE") return TRADE;
if(s == "CHEAT") return CHEAT;
if(s == "EMPTY") return EMPTY;
assert(false);
}
ACTION getMemory(string addr) {
if (addr == "LAST1")
return R[LAST1];
if (addr == "LAST2")
return R[LAST2];
assert(false);
}
void record(ACTION a) {
ACTION last1 = R[LAST1], last2 = R[LAST2];
R[LAST1] = a;
R[LAST2] = last1;
}
};
class Strategy {
public:
char prog[1024];
Strategy(string s) {
for (int i = 0; i < s.length(); i++) {
prog[i] = s[i];
}
prog[s.length()] = '\0';
}
char* match(char *str, const char *p) {
if (str == NULL)
return NULL;
if (memcmp(str, p, strlen(p)) == 0)
return str + strlen(p);
return NULL;
}
/*
<condition> ::= <cond> | <cond> <op> <condition>
<op> ::= AND | OR
<cond> ::= <memory> {= | #} {<command> | NULL}
<memory> ::= {MY | YOUR} LAST {1 | 2}
<command> ::= TRADE | CHEAT
*/
bool cond(char* &p, Memory MY, Memory YOUR) {
// printf("condition %s\n", p);
char *q;
Memory *addr = NULL;
string read, equal, kind;
/* <memory> */
q = match(p, "MY");
if(q != NULL) p = q, addr = &MY;
q = match(p, "YOUR");
if(q != NULL) p = q, addr = &YOUR;
q = match(p, "LAST1");
if(q != NULL) p = q, read = "LAST1";
q = match(p, "LAST2");
if(q != NULL) p = q, read = "LAST2";
/* {= | #} */
q = match(p, "=");
if(q != NULL) p = q, equal = "=";
q = match(p, "#");
if(q != NULL) p = q, equal = "#";
/* {<command> | NULL} */
q = match(p, "TRADE");
if(q != NULL) p = q, kind = "TRADE";
q = match(p, "CHEAT");
if(q != NULL) p = q, kind = "CHEAT";
q = match(p, "NULL");
if(q != NULL) p = q, kind = "EMPTY";
bool ret = equal == "=" ? addr->getMemory(read) == Memory::getAction(kind) :
addr->getMemory(read) != Memory::getAction(kind);
// cout << "ADDR: " << addr->getMemory(read) << ", ACTION: " << Memory::getAction(kind) << endl;
// cout << read << ", " << kind << endl;
/* <op> <condition> */
q = match(p, "AND");
if(q != NULL) p = q, ret = ret&cond(p, MY, YOUR);
q = match(p, "OR");
if(q != NULL) p = q, ret = ret|cond(p, MY, YOUR);
// cout << "return " << ret << endl;
return ret;
}
ACTION ifstat(char* &p, Memory a, Memory b) {
char *q;
q = match(p, "IF");
if (q != NULL) {
p = q;
bool f = cond(p, a, b);
ACTION a1, a2;
q = match(p, "THEN");
p = q;
a1 = statement(p, a, b);
q = match(p, "ELSE");
p = q;
a2 = statement(p, a, b);
return f ? a1 : a2;
}
assert(false);
}
ACTION statement(char* &p, Memory a, Memory b) {
// printf("%s\n", p);
char *q;
q = match(p, "TRADE");
if (q != NULL) {
p = q;
return TRADE;
}
q = match(p, "CHEAT");
if (q != NULL) {
p = q;
return CHEAT;
}
return ifstat(p, a, b);
}
ACTION eval(char* &p, Memory a, Memory b) { // read only
// printf("eval %s\n", p);
return statement(p, a, b);
}
};
int main() {
char c, s[1024];
vector<Strategy> machine;
while (true) {
int n = 0;
while ((c = getchar()) != EOF) {
if(c == '.' || (c == '#' && n == 0) )
break;
if(isspace(c))
continue;
s[n++] = c;
}
if (n == 0)
break;
s[n++] = '\0';
machine.push_back(Strategy(s));
}
#define MAXN 20
int SCORE[MAXN] = {};
for (int i = 0; i < machine.size(); i++) {
for (int j = i + 1; j < machine.size(); j++) {
Memory ma, mb;
Strategy &A = machine[i];
Strategy &B = machine[j];
char *p;
for (int k = 0; k < 10; k++) {
ACTION ia = A.eval(p = A.prog, ma, mb);
ACTION ib = B.eval(p = B.prog, mb, ma);
if(ia == TRADE && ib == TRADE)
SCORE[i]++, SCORE[j]++;
else if(ia == TRADE && ib == CHEAT)
SCORE[i] -= 2, SCORE[j] += 2;
else if(ia == CHEAT && ib == TRADE)
SCORE[i] += 2, SCORE[j] -= 2;
else
SCORE[i]--, SCORE[j]--;
ma.record(ia), mb.record(ib);
}
}
}
for(int i = 0; i < machine.size(); i++)
printf("%3d\n", SCORE[i]);
return 0;
}