-
Notifications
You must be signed in to change notification settings - Fork 21
/
gatosolver.cpp
148 lines (118 loc) · 3.72 KB
/
gatosolver.cpp
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
#include <QDebug>
#include "gatosolver.h"
#include "gatorom.h"
#include "gatodecodercolsdownl.h"
#include "gatodecodercolsdownr.h"
#include "gatodecodercolsleft.h"
#include "gatodecodercolsright.h"
#include "gatodecodersqueezelr.h"
#include "gatodecodertlcsfont.h"
#include "gatodecodercolsdownlswap.h" //NEC uCOM4
#include "gatodecoderz86x1.h"
// Default solver is a dummy state that covers everything.
GatoGraderAll::GatoGraderAll(){}
// Approves of every state.
int GatoGraderAll::grade(QByteArray ba){
return 100;
}
GatoGrader::~GatoGrader(){
}
GatoSolver::GatoSolver(GatoROM *rom, GatoGrader *grader){
assert(grader);
assert(rom);
this->rom=rom;
this->grader=grader;
//We can grow this table when it becomes too large.
decoders[0]=new GatoDecoderColsLeft();
decoders[1]=new GatoDecoderColsRight();
decoders[2]=new GatoDecoderSqueezeLR();
decoders[3]=new GatoDecoderColsDownL();
decoders[4]=new GatoDecoderColsDownR();
decoders[5]=new GatoDecoderTLCSFont();
decoders[6]=new GatoDecoderZ86x1();
decoders[7]=new GatoDecoderColsDownLSwap(); //NEC uCOM4
//Remainder of table must be null.
decoders[8]=0;
decoders[9]=0;
decoders[10]=0;
decoders[11]=0;
decoders[12]=0;
decoders[13]=0;
decoders[14]=0;
decoders[15]=0;
}
//Initialize the solver to its first state.
void GatoSolver::init(){
state=0;
applyState();
}
//Step to the next state. Returns 0 when complete.
bool GatoSolver::next(){
state++;
return applyState();
}
//Packing works differently on each compiler.
#ifdef _MSC_VER
#define PACK( __Declaration__ ) __pragma( pack(push, 1) ) __Declaration__ __pragma( pack(pop))
#elif defined(__GNUC__)
#define PACK( __Declaration__ ) __Declaration__ __attribute__((__packed__))
#endif
//This is a little lazy, might cause bugs on big endian machines.
PACK(struct statefield {
unsigned char rotation : 2;
unsigned char flipx : 1;
//unsigned char flipy : 1;
unsigned char invert : 1;
unsigned char bank : 2; //No banking, left or right banking.
unsigned char decoder : 4; //Always the last thing we iterate.
unsigned char toohigh : 1; //Zero until the others have overflowed.
});
//Are we there yet?
bool GatoSolver::finished(){
statefield *state=(statefield*) &(this->state);
return state->toohigh;
}
bool GatoSolver::applyState(){
/* Basically the state is a big-ass integer that contains bitfields
* for all relevant settings. Two bits for the rotation, so many bits
* for the decoder type, etc. By scanning all values of it, we can
* attempt every valid decoding of a ROM.
*/
statefield *state=(statefield*) &(this->state);
while(!state->toohigh){ //Overflow condition.
switch(state->rotation){
case 0:
rom->rotate(0,true);
break;
case 1:
rom->rotate(90,true);
break;
case 2:
rom->rotate(180,true);
break;
case 3:
rom->rotate(270,true);
break;
}
rom->bank=state->bank;
rom->flipx(state->flipx);
rom->invert(state->invert);
rom->decoder=decoders[state->decoder];
//We'll have invalid states within the table when the decoder is null.
if(rom->decoder && rom->bank!=3){
rom->decode(); //Implies an eval().
return true;
}
//If the state is invalid, quietly try the next.
this->state++;
//state=(statefield*) &(this->state); //No need, because the pointer aims here.
}
return false;
}
//Get the grade, from -1 to 100.
int GatoSolver::grade(){
assert(grader);
assert(rom);
int grade=grader->grade(rom->decoded);
return grade;
}