Zitat von
Rabenauge
@Hawe: ich fürchte, ich komme mit deinem Programmierstil (soll keinerlei Kritik sein!) nicht klar- aber ja, würde mich interessieren.
Goto's hab ich das letzte Mal vor, hm, sagen wir, 25 Jahren benutzt, als ich mich noch mit BASIC beschäftigt hab.
Zwischendurch hatte ich einige Male durchaus den Gedanken "man, ein >Goto< würde das jetzt ganz fix lösen"- aber ich konnt es ausnahmslos vermeiden.
Von daher: nein, man braucht es definitiv nicht in Arduino-Slang, würd ich sagen.
Zugegeben: es ergibt ohne manchmal ein paar Zeilen Code mehr, aber das ist egal, wichtig ist nur, was am Ende (compiliert) dabei raus kommt, und die Compiler sind inzwischen ziemlich clevere Kerlchen geworden.
Ich erinnere mich noch dunkel an den uralten Bascom (lief damals auf meinem Robotron A5110 unter SCP)- das war ne ganz andere, eigene Welt....
schau mal, ob du die gotos zu den beiden Sprungmarken A: und C: in der rekursiven Funktion hier rauskriegst (ich finde es zumindest klarer, eindeutiger und übersichtlicher damit)...
(dies erste hier ist die Original-Ideen-Vorlage zu meiner Chess-Engine)
Code:
// http://home.hccnet.nl/h.g.muller/max-src2.html
#define F(I,S,N) for(I=S;I<N;I++)
#define W(A) while(A)
#define K(A,B) *(int*)(T+A+(B&8)+S*(B&7))
#define J(A) K(y+A,b[y])-K(x+A,u)-K(H+A,t)
#define U 16777224
struct _ {int K,V;char X,Y,D;} A[U]; /* hash table, 16M+8 entries*/
int V=112,M=136,S=128,I=8e4,C=799,Q,N,i; /* V=0x70=rank mask, M=0x88 */
char O,K,L,
w[]={0,1,1,3,-1,3,5,9}, /* relative piece values */
o[]={-16,-15,-17,0,1,16,0,1,16,15,17,0,14,18,31,33,0, /* step-vector lists */
7,-1,11,6,8,3,6, /* 1st dir. in o[] per piece*/
6,3,5,7,4,5,3,6}, /* initial piece setup */
b[129], /* board: half of 16x8+dummy*/
T[1035], /* hash translation table */
n[]=".?+nkbrq?*?NKBRQ"; /* piece symbols on printout*/
D(k,q,l,e,J,Z,E,z,n) /* recursive minimax search, k=moving side, n=depth*/
int k,q,l,e,J,Z,E,z,n; /* (q,l)=window, e=current eval. score, E=e.p. sqr.*/
{ /* e=score, z=prev.dest; J,Z=hashkeys; return score*/
int j,r,m,v,d,h,i=9,F,G;
char t,p,u,x,y,X,Y,H,B;
struct _*a=A;
/* lookup pos. in hash table*/
j=(k*E^J)&U-9; /* try 8 consec. locations */
W((h=A[++j].K)&&h-Z&&--i); /* first empty or match */
a+=i?j:0; /* dummy A[0] if miss & full*/
if(a->K) /* hit: pos. is in hash tab */
{d=a->D;v=a->V;X=a->X; /* examine stored data */
if(d>=n) /* if depth sufficient: */
{if(v>=l|X&S&&v<=q|X&8)return v; /* use if window compatible */
d=n-1; /* or use as iter. start */
}X&=~M;Y=a->Y; /* with best-move hint */
Y=d?Y:0; /* don't try best at d=0 */
}else d=X=Y=0; /* start iter., no best yet */
N++; /* node count (for timing) */
W(d++<n|z==8&N<1e7&d<98) /* iterative deepening loop */
{x=B=X; /* start scan at prev. best */
Y|=8&Y>>4; /* request try noncastl. 1st*/
m=d>1?-I:e; /* unconsidered:static eval */
do{u=b[x]; /* scan board looking for */
if(u&k) /* own piece (inefficient!)*/
{r=p=u&7; /* p = piece type (set r>0) */
j=o[p+16]; /* first step vector f.piece*/
W(r=p>2&r<0?-r:-o[++j]) /* loop over directions o[] */
{A: /* resume normal after best */
y=x;F=G=S; /* (x,y)=move, (F,G)=castl.R*/
do{H=y+=r; /* y traverses ray */
if(Y&8)H=y=Y&~M; /* sneak in prev. best move */
if(y&M)break; /* board edge hit */
if(p<3&y==E)H=y^16; /* shift capt.sqr. H if e.p.*/
t=b[H];if(t&k|p<3&!(r&7)!=!t)break; /* capt. own, bad pawn mode */
i=99*w[t&7]; /* value of capt. piece t */
if(i<0||E-S&&b[E]&&y-E<2&E-y<2)m=I; /* K capt. or bad castling */
if(m>=l)goto C; /* abort on fail high */
if(h=d-(y!=z)) /* remaining depth(-recapt.)*/
{v=p<6?b[x+8]-b[y+8]:0; /* center positional pts. */
b[G]=b[H]=b[x]=0;b[y]=u&31; /* do move, strip virgin-bit*/
if(!(G&M)){b[F]=k+6;v+=30;} /* castling: put R & score */
if(p<3) /* pawns: */
{v-=9*(((x-2)&M||b[x-2]!=u)+ /* structure, undefended */
((x+2)&M||b[x+2]!=u)-1); /* squares plus bias */
if(y+r+1&S){b[y]|=7;i+=C;} /* promote p to Q, add score*/
}
v=-D(24-k,-l-(l>e),m>q?-m:-q,-e-v-i, /* recursive eval. of reply */
J+J(0),Z+J(8)+G-S,F,y,h); /* J,Z: hash keys */
v-=v>e; /* delayed-gain penalty */
if(z==9) /* called as move-legality */
{if(v!=-I&x==K&y==L) /* checker: if move found */
{Q=-e-i;O=F;return l;} /* & not in check, signal */
v=m; /* (prevent fail-lows on */
} /* K-capt. replies) */
b[G]=k+38;b[F]=b[y]=0;b[x]=u;b[H]=t; /* undo move,G can be dummy */
if(Y&8){m=v;Y&=~8;goto A;} /* best=1st done,redo normal*/
if(v>m){m=v;X=x;Y=y|S&G;} /* update max, mark with S */
} /* if non castling */
t+=p<5; /* fake capt. for nonsliding*/
if(p<3&6*k+(y&V)==S /* pawn on 3rd/6th, or */
||(u&~24)==36&j==7&& /* virgin K moving sideways,*/
G&M&&b[G=(x|7)-(r>>1&7)]&32 /* 1st, virgin R in corner G*/
&&!(b[G^1]|b[G^2]) /* 2 empty sqrs. next to R */
){F=y;t--;} /* unfake capt., enable e.p.*/
}W(!t); /* if not capt. continue ray*/
}}}W((x=x+9&~M)-B); /* next sqr. of board, wrap */
C:if(m>I/4|m<-I/4)d=99; /* mate is indep. of depth */
m=m+I?m:-D(24-k,-I,I,0,J,Z,S,S,1)/2; /* best loses K: (stale)mate*/
if(!a->K|(a->X&M)!=M|a->D<=d) /* if new/better type/depth:*/
{a->K=Z;a->V=m;a->D=d;A->K=0; /* store in hash,dummy stays*/
a->X=X|8*(m>q)|S*(m<l);a->Y=Y; /* empty, type (limit/exact)*/
} /* encoded in X S,8 bits */
/*if(z==8)printf("%2d ply, %9d searched, %6d by (%2x,%2x)\n",d-1,N,m,X,Y&0x77);*/
}
if(z&8){K=X;L=Y&~M;}
return m;
}
main()
{
int j,k=8,*p,c[9];
F(i,0,8)
{b[i]=(b[i+V]=o[i+24]+40)+8;b[i+16]=18;b[i+96]=9; /* initial board setup*/
F(j,0,8)b[16*j+i+8]=(i-4)*(i-4)+(j-3.5)*(j-3.5); /* center-pts table */
} /*(in unused half b[])*/
F(i,M,1035)T[i]=random()>>9;
W(1) /* play loop */
{F(i,0,121)printf(" %c",i&8&&(i+=7)?10:n[b[i]&15]); /* print board */
p=c;W((*p++=getchar())>10); /* read input line */
N=0;
if(*c-10){K=c[0]-16*c[1]+C;L=c[2]-16*c[3]+C;}else /* parse entered move */
D(k,-I,I,Q,1,1,O,8,0); /* or think up one */
F(i,0,U)A[i].K=0; /* clear hash table */
if(D(k,-I,I,Q,1,1,O,9,2)==I)k^=24; /* check legality & do*/
}
}
und hier meine 1. Portierung zu Arduino-C++ (auch ein klein wenig leserlicher, und die Sprungmarken heissen hier ausgeschrieben labelA und labelC):
Code:
// Code für Arduino Due
#define K(A,B) *(int*)(T+A+(B&8)+S*(B&7))
#define HTsize (1<<12) // wegen RAM, für PC: (1<<24) // <<<<<< für Arduino Mega verkleinern auf 1<<8 !
struct HTab {
int K,
V;
int X,
Y,
D;
} HTarray[HTsize]; /* hash table, HTsize entries*/
#define MAXNODES 80000 // wegen Zugdauer; für PC: x10 = 800000 = 8e5 // <<<<<< für Mega verkleinern auf 10000!
int K,
Q,
R,
J,
Z;
int32_t N,
I=80000; /* I=80000: "infinity score" */ ;
int
M=136, /* M=0x88 board system */
S=128, /* dummy square 0x80, highest valid square =127 */
turn=16; // 16=Weiss, 8=Schwarz; turn^=24 wechselt hin unnd her
signed char L,
pval[]={0,2,2,7,-1,8,12,23}, /* relative piece values */
vector[]={-16,-15,-17,0,1,16,0,1,16,15,17,0,14,18,31,33,0, /* step-vector lists */
7,-1,11,6,8,3,6}, /* 1st dir. in vector[] per piece*/
bsetup[]={6,3,5,7,4,5,3,6}, /* initial piece setup */
board[129], /* board: half of 16x8+dummy*/
T[1035]; /* hash translation table */
signed char psymbol[]= ".?+nkbrq?*?NKBRQ";
int mfrom, mto; // current ply from - to
int EPSQ, // e.p. square
RemP; // remove piece
/* recursive minimax search, turn=moving side, n=depth*/
int Minimax(int32_t q, int32_t l, int32_t score, int EPC, int prev, int32_t n)
/* (q,l)=window, score, EnPass_sqr.*/
/* prev=prev.dest; J,Z=hashkeys; return score*/
{
int j,
r,
m,
v,
d,
h,
i,
F,
G,
V,
P,
f=J,
g=Z,
C,
s;
signed char t,
p,
upiece,
x,
y,
X,
Y,
H,
B;
struct HTab *a = HTarray + (J + turn * EPC & HTsize-1); /* lookup pos. in hash table*/
char sbuf[50];
q--; /* adj. window: delay bonus */
turn^=24; /* change sides */
d=a->D;
m=a->V;
X=a->X;
Y=a->Y; /* resume at stored depth */
if(a->K-Z|prev| /* miss: other pos. or empty*/
!(m<=q | X&8&&m>=l | X&S)) /* or window incompatible */
{ d=Y=0; } /* start iter. from scratch */
X&=~M; /* start at best-move hint */
while( d++ < n || d<3 /* iterative deepening loop */
|| prev&K == I
&& ( N<60000 & d<98 /* root: deepen upto time */
|| (K=X, L=Y&~M, d=3)
)
) /* time's up: go do best */
{
x=B=X; /* start scan at prev. best */
h=Y&S; /* request try noncastl. 1st*/
P=d<3 ? I : Minimax(-l,1-l,-score,S,0,d-3); /* Search null move */
m = (-P<l | R>35) ? ( d>2 ? -I : score ) : -P; /* Prune or stand-pat */
N++; /* node count (for timing) */
do
{
upiece=board[x]; /* scan board looking for */
if(upiece & turn) /* own piece (inefficient!)*/
{
r = p = upiece&7; /* p = piece type (set r>0) */
j = vector[p+16]; /* first step vector f.piece*/
while(r = p>2 & r<0 ? -r : -vector[++j] ) /* loop over directions vector[] */
{
labelA: /* resume normal after best */
y=x; /* (x,y)=move */
F=G=S; /* (F,G)=castl.R */
do
{ /* y traverses ray, or: */
H=y=h?Y^h:y+r; /* sneak in prev. best move */
if(y&M)break; /* board edge hit */
m= EPC-S&board[EPC]&&y-EPC<2&EPC-y<2?I:m; /* bad castling */
if(p<3&y==EPC)H^=16; /* shift capt.sqr. H if e.p.*/
t=board[H];
if(t&turn|p<3&!(y-x&7)-!t)break; /* capt. own, bad pawn mode */
i=37*pval[t&7]+(t&192); /* value of capt. piece t */
m=i<0?I:m; /* K capture */
if(m>=l&d>1) goto labelC; /* abort on fail high */
v=d-1?score:i-p; /* MVV/LVA scoring */
if(d-!t>1) /* remaining depth */
{
v=p<6?board[x+8]-board[y+8]:0; /* center positional pts. */
board[G]=board[H]=board[x]=0;board[y]=upiece|32; /* do move, set non-virgin */
if(!(G&M))board[F]=turn+6,v+=50; /* castling: put R & score */
v-=p-4|R>29?0:20; /* penalize mid-game K move */
if(p<3) /* pawns: */
{
v-=9*((x-2&M||board[x-2]-upiece)+ /* structure, undefended */
(x+2&M||board[x+2]-upiece)-1 /* squares plus bias */
+(board[x^16]==turn+36)) /* kling to non-virgin King */
-(R>>2); /* end-game Pawn-push bonus */
V=y+r+1&S?647-p:2*(upiece&y+16&32); /* promotion or 6/7th bonus */
board[y]+=V;
i+=V; /* change piece, add score */
}
v+= score+i;
V=m>q ? m : q; /* new eval and alpha */
J+=K(y+0,board[y])-K(x+0,upiece)-K(H+0,t);
Z+=K(y+8,board[y])-K(x+8,upiece)-K(H+8,t)+G -S; /* update hash key */
C=d-1-(d>5&p>2&!t&!h);
C=R>29|d<3|P-I?C:d; /* extend 1 ply if in check */
do {
s=C>2|v>V?-Minimax(-l,-V,-v, /* recursive eval. of reply */
F,0,C):v; /* or fail low if futile */
} while( s>q & ++C<d );
v=s;
if(prev&&K-I&&v+I&&x==K&y==L) /* move pending & in root: */
{
Q=-score-i; EPSQ=F; /* exit if legal & found */
a->D=99;a->V=0; /* lock game in hash as draw*/
R+=i>>7;
return l; /* captured non-P material */
}
J=f;
Z=g; /* restore hash key */
board[G]=turn+6;
board[F]=board[y]=0;
board[x]=upiece;
board[H]=t; /* undo move,G can be dummy */
}
if(v>m) /* new best, update max,best*/
{
m=v,X=x,Y=y|S&F; /* mark double move with S */
}
if(h)
{
h=0;
goto labelA; /* redo after doing old best*/
}
if (
x+r-y|upiece&32| /* not 1st step,moved before*/
p>2 & (
p-4|j-7|| /* no P & no lateral K move,*/
board[G=x+3^r>>1&7]-turn-6 /* no virgin R in corner G, */
|| board[G^1] | board[G^2] ) /* no 2 empty sq. next to R */
)
{
t+=p<5;
} /* fake capt. for nonsliding*/
else F=y; /* enable e.p. */
} while(!t); /* if not capt. continue ray*/
}
} // (upiece & turn)
} while((x=x+9&~M)-B); /* next sqr. of board, wrap */
labelC:
if (m>I-M|m<M-I) d=98; /* mate holds to any depth */
m= m+I|P==I ? m : 0; /* best loses K: (stale)mate*/
if(a->D<99) { /* protect game history */
a->K=Z;
a->V=m;
a->D=d; /* always store in hash tab */
a->X=X|8*(m>q)|S*(m<l);
a->Y=Y; /* move, type (bound/exact),*/
}
/* uncomment for Kibitz */
//if(prev) sprintf(sbuf, "%2d ply, %9d searched, score=%6d by %c%c%c%c\n",
// d-1, N-S, m, 'a'+(X&7),'8'-(X>>4),'a'+(Y&7),'8'-(Y>>4&7));
if(prev && X!=Y) {
sprintf(sbuf, "\n%2d ply, searched: %9d ", d-1, N-S );
Serial.print(sbuf);
}
else
if( ((N-S)%10000)<1) Serial.print(".");
} // while (iterative deepening loop)
turn^=24; /* change sides back */
mfrom=K; mto=L;
return m+= m<score; /* delayed-loss bonus */
}
void chess()
{
int32_t score, i;
int16_t oldto, oldEPSQ;
char sbuf[50], sbuf2[50];
char cstring[20];
signed char oboard[129], spiece;
K=8;
while(K--)
{
board[K]=(board[K+112]=bsetup[K]+8)+8;
board[K+16]=18;
board[K+96]=9; /* initial board setup*/
L=8;
while(L--)board[16*L+K+8]=(K-4)*(K-4)+(L-3.5)*(L-3.5); /* center-pts table */
} /*(in unused half board[])*/
N=1035;
while(N-->M)T[N]=rand()>>9;
/* play loop */
while(1)
{
N=-1;
Serial.print("\n");
sprintf(sbuf," A B C D E F G H \n --------------- \n"); Serial.print(sbuf);
while(++N<121) { /* print board */
if(N&8 && (N+7!=0) ) {sprintf(sbuf,"%3d \n", 1+((120-N)>>4)); Serial.print(sbuf); N+=7; }
else {
if(N%8==0) {sprintf(sbuf, "%3d ", 1+((120-N)>>4)); Serial.print(sbuf); }
sprintf(sbuf," %c", psymbol[board[N]&15]); Serial.print(sbuf);
}
}
sprintf(sbuf," --------------- \n A B C D E F G H "); Serial.print(sbuf);
if(turn==16) sprintf(sbuf,"\n> WHITE: "); else sprintf(sbuf,"\n> BLACK: ");
Serial.print(sbuf);
i = 0;
strcpy(cstring,"");
do {
while (Serial.available()==0);
cstring[i] = Serial.read();
if(cstring[i]==13) {
cstring[i]=0;
break;
}
else i++;
} while(i < 10);
K=I;
if(cstring[0]!=0) { /* parse entered move */
K= cstring[0]-16*cstring[1]+799;
L= cstring[2]-16*cstring[3]+799;
}
/*
Serial.print("\n DEBUG cstring : "); Serial.print(cstring);
sprintf(sbuf,"\n DEBUG K: %d \n DEBUG L: %d \n", K, L);
Serial.print(sbuf);
*/
memcpy(oboard, board, sizeof(board));
oldto=mto;
oldEPSQ=EPSQ;
score=Minimax(-I, I, Q, EPSQ, 1, 3); /* think or check & do*/
if(score!=15) {
RemP=S;
if(oboard[mto]) RemP=mto;
if(mto==oldEPSQ) RemP=oldto;
spiece=psymbol[board[mto]&15];
if(spiece=='*' || spiece=='+') spiece=' ';
sprintf(sbuf,"\n\nmoved: >> %c %c%c", spiece,'a'+(mfrom&7),'8'-(mfrom>>4) );
if(oboard[mto]) strcat(sbuf," X ");
else strcat(sbuf,"-");
sprintf(sbuf2,"%c%c ", 'a'+(mto&7),'8'-(mto>>4&7));
strcat(sbuf, sbuf2);
Serial.print(sbuf);
sprintf(sbuf, " \nDEBUG: %d to %d \n", mfrom, mto);
Serial.print(sbuf);
sprintf(sbuf," EPsq: %c%c (%3d)\n",
'a'+(EPSQ&7), '8'-(EPSQ>>4&7), EPSQ); Serial.print(sbuf);
sprintf(sbuf," RemP: %c%c (%3d)\n\n",
'a'+(RemP&7), '8'-(RemP>>4&7), RemP); Serial.print(sbuf);
}
else printf("\n\nILLEGAL!\n");
}
}
void setup() {
Serial.begin(9600);
}
void loop() {
chess();
while(1);
}
------------------
PS:
aber ganz abgesehen von der Lösbarkeit:
goto ist und bleibt legaler C code, es gibt keinen Grund, ihn zu zu verteufeln oder grundsätzlich stattdessen if/else if oder case/switch oder verschachtelte for/while/break/continue vorzuziehen. Auch case/switch/break ist übrigens nichts anderes als ein verkapptes goto (denn die "case" Fälle sind in Wirklichkeit für den Compiler Konstanten, die als goto-Sprungmarken benutzt werden, nichts anderes).
Er ist "riskanter", ok, aber C ist grundsätzlich riskant, weil C nunmal - gottseidank - alles erlaubt, was riskant ist, schon angefangen mit Schreiben von Werten an Pointer Adressen und deren Referenzen, wodurch man schnell im Nirwana landen kann.
In C entscheidet aber eben einzig der Programmierer, was erlaubt ist oder sein soll, nicht die Programmiersprache oder der Compiler, notfalls kann man es sogar per Compiler Flags erzwingen (-fpermissive etc):
und das finde ich absolut gut so, und daher ziehe ich auch nicht grundsätzlich irgend was anderes dem goto generell vor, sondern entscheide nach Praktikabilität und notfalls Bequemlichkeit. Wer die Fallstricke kennt, kann sie auch vermeiden, wer nicht, muss halt aufpassen. Nachfragen in Foren helfen einem dann aber wiederum schnell bei der persönlichen Lernkurve
Lesezeichen