#include "defs.h" /* to add a new weighting factor: 1. Add a declaration of a new variable with name Weight to this file. 2. Add a new panel slider for setting/seeing its value, modeled after the others. Note that you can use the same notify proc. 3. Write an evaluation function, eval, which returns more negative values for worse positions. Never return a value > 0. 4. Add a new variable to 'struct evalVals' in autoplay.c. 5. Add a line to proc 'evalPiece' to call the eval func, multiply by the weight, and add to the total. 6. Add prints of your new value to the printf and sprintf in autoplay.c where everything else is printed. 7. Increase the value of XV_WIDTH in the call of 'xv_create(explainFrame,...' to hold the new value (just estimate). 8. Save the value into aux_table in routine 'catch_aux'. 9. Add the new weight into the declaration of 'struct aux_score_table' in defs.h 10. Bump the value of MYVERSION in defs.h. 11. In file score.c: a. scan the new weight in 'decode_aux'. b. print the new weight also in 'print_aux', (at the same position as in decode_aux!). That should do it. */ #undef min #undef max #define NUM_WEIGHTS 5 #define STARTWEIGHT 5 int RowElimWeight = 1; int MaxFlatWeight = 0; int VariationWeight = 3; int HolesWeight = 40; int GameOverWeight = 10; int FissureWeight = 8; int ExposeHoleWeight = 2; int RowElimProgressWeight = 2; int MaxHeightWeight = 1; int LookAheadWeight = 10; int HolesBeforeLookahead = 10; int instantdrop = 1; int autoVaryRowElim = 1; int signalEndOfGame = 1; int autorestart = 1; int autodump = 0; struct evalVals { int xpos, ypos, rot; int rowelim, maxflat, variation, holes, gameover, fissure, exposehole, rowelimprogress, maxheight, lookAhead; int totalscore; } evalPiece(); static Panel autocontrolpanel; static Frame autocontrolframe; typedef struct EvalContext { int oldholecount; int rememberedHoleY; int lookaheaddone; int shape_no; int heightTable[UWIDTH]; int fullTable[UHEIGHT]; unsigned char grid[UWIDTH][UHEIGHT]; } *Context; static Frame explainFrame; static Canvas explainCanvas; static struct explainclientData { int count; } realclientData; int PanelWeightProc(), explainProc(), explainDoneProc(), explainCanvasBuildCallback(); Pixfont *tinyfont; extern Pixfont *xv_pf_open(); initautocontrol() { tinyfont = xv_pf_open("-*-*-medium-r-normal-*-8-*-*-*-*-*-*-*"); autocontrolframe = xv_create(mainframe, FRAME, FRAME_SHOW_LABEL, TRUE, XV_LABEL, "Control for Tetris Auto-play - weiser", 0); autocontrolpanel = xv_create(autocontrolframe, PANEL, 0); (void) xv_create(autocontrolpanel, PANEL_BUTTON, PANEL_LABEL_IMAGE, panel_button_image(autocontrolpanel, "Explain", 7, 0), PANEL_NOTIFY_PROC, explainProc, XV_SHOW, TRUE, 0); (void) xv_create(autocontrolpanel, PANEL_TOGGLE, PANEL_CHOICE_STRINGS, "Fast Drop", 0, PANEL_NOTIFY_PROC, PanelWeightProc, XV_SHOW, TRUE, PANEL_CLIENT_DATA, &instantdrop, PANEL_VALUE, instantdrop, 0); (void) xv_create(autocontrolpanel, PANEL_TOGGLE, PANEL_CHOICE_STRINGS, "Dribble", 0, PANEL_NOTIFY_PROC, PanelWeightProc, XV_SHOW, TRUE, PANEL_CLIENT_DATA, &autodump, PANEL_VALUE, autodump, 0); (void) xv_create(autocontrolpanel, PANEL_TOGGLE, PANEL_CHOICE_STRINGS, "Auto-restart", 0, PANEL_NOTIFY_PROC, PanelWeightProc, XV_SHOW, TRUE, PANEL_CLIENT_DATA, &autorestart, PANEL_VALUE, autorestart, 0); (void) xv_create(autocontrolpanel, PANEL_TOGGLE, PANEL_CHOICE_STRINGS, "don't display moves", 0, PANEL_NOTIFY_PROC, PanelWeightProc, XV_SHOW, TRUE, PANEL_CLIENT_DATA, &nodisplay, PANEL_VALUE, nodisplay, 0); (void) xv_create(autocontrolpanel, PANEL_TOGGLE, PANEL_CHOICE_STRINGS, "Don't display scores", 0, PANEL_NOTIFY_PROC, PanelWeightProc, XV_SHOW, TRUE, PANEL_CLIENT_DATA, &noscores, PANEL_VALUE, noscores, 0); (void) xv_create(autocontrolpanel, PANEL_TOGGLE, PANEL_CHOICE_STRINGS, "Explain leaving holes", 0, PANEL_NOTIFY_PROC, PanelWeightProc, XV_SHOW, TRUE, PANEL_CLIENT_DATA, &explainholes, PANEL_VALUE, explainholes, 0); (void) xv_create(autocontrolpanel, PANEL_TOGGLE, PANEL_CHOICE_STRINGS, "Signal end-of-game", 0, PANEL_NOTIFY_PROC, PanelWeightProc, XV_SHOW, TRUE, PANEL_CLIENT_DATA, &signalEndOfGame, PANEL_VALUE, signalEndOfGame, 0); rowElimItem = xv_create(autocontrolpanel, PANEL_SLIDER, PANEL_LABEL_STRING, "Row Elimination", PANEL_MIN_VALUE, 0, PANEL_MAX_VALUE, 10, PANEL_SHOW_RANGE, TRUE, PANEL_SHOW_VALUE, TRUE, PANEL_VALUE, RowElimWeight, XV_SHOW, TRUE, PANEL_CLIENT_DATA, &RowElimWeight, PANEL_NOTIFY_PROC, PanelWeightProc, 0); (void) xv_create(autocontrolpanel, PANEL_SLIDER, PANEL_LABEL_STRING, "Max Flat", PANEL_MIN_VALUE, 0, PANEL_MAX_VALUE, 10, PANEL_SHOW_RANGE, TRUE, PANEL_SHOW_VALUE, TRUE, PANEL_VALUE, MaxFlatWeight, PANEL_CLIENT_DATA, &MaxFlatWeight, PANEL_NOTIFY_PROC, PanelWeightProc, 0); (void) xv_create(autocontrolpanel, PANEL_SLIDER, PANEL_LABEL_STRING, "Height Variation", PANEL_MIN_VALUE, 0, PANEL_MAX_VALUE, 10, PANEL_SHOW_RANGE, TRUE, PANEL_SHOW_VALUE, TRUE, PANEL_VALUE, VariationWeight, PANEL_CLIENT_DATA, &VariationWeight, PANEL_NOTIFY_PROC, PanelWeightProc, 0); holesItem = xv_create(autocontrolpanel, PANEL_SLIDER, PANEL_LABEL_STRING, "Holes", PANEL_MIN_VALUE, 0, PANEL_MAX_VALUE, 40, PANEL_SHOW_RANGE, TRUE, PANEL_SHOW_VALUE, TRUE, PANEL_VALUE, HolesWeight, PANEL_CLIENT_DATA, &HolesWeight, PANEL_NOTIFY_PROC, PanelWeightProc, 0); (void) xv_create(autocontrolpanel, PANEL_SLIDER, PANEL_LABEL_STRING, "Fissures", PANEL_MIN_VALUE, 0, PANEL_MAX_VALUE, 10, PANEL_SHOW_RANGE, TRUE, PANEL_SHOW_VALUE, TRUE, PANEL_VALUE, FissureWeight, PANEL_CLIENT_DATA, &FissureWeight, PANEL_NOTIFY_PROC, PanelWeightProc, 0); (void) xv_create(autocontrolpanel, PANEL_SLIDER, PANEL_LABEL_STRING, "Game Over", PANEL_MIN_VALUE, 0, PANEL_MAX_VALUE, 10, PANEL_SHOW_RANGE, TRUE, PANEL_SHOW_VALUE, TRUE, PANEL_VALUE, GameOverWeight, PANEL_CLIENT_DATA, &GameOverWeight, PANEL_NOTIFY_PROC, PanelWeightProc, 0); (void) xv_create(autocontrolpanel, PANEL_SLIDER, PANEL_LABEL_STRING, "Expose Hole", PANEL_MIN_VALUE, 0, PANEL_MAX_VALUE, 10, PANEL_SHOW_RANGE, TRUE, PANEL_SHOW_VALUE, TRUE, PANEL_VALUE, ExposeHoleWeight, PANEL_CLIENT_DATA, &ExposeHoleWeight, PANEL_NOTIFY_PROC, PanelWeightProc, 0); rowElimProgressItem = xv_create(autocontrolpanel, PANEL_SLIDER, PANEL_LABEL_STRING, "Row Elim Progress", PANEL_MIN_VALUE, 0, PANEL_MAX_VALUE, 10, PANEL_SHOW_RANGE, TRUE, PANEL_SHOW_VALUE, TRUE, PANEL_VALUE, RowElimProgressWeight, PANEL_CLIENT_DATA, &RowElimProgressWeight, PANEL_NOTIFY_PROC, PanelWeightProc, 0); (void) xv_create(autocontrolpanel, PANEL_TOGGLE, PANEL_CHOICE_STRINGS, "AutoVaryRowElimByHeight", 0, PANEL_NOTIFY_PROC, PanelWeightProc, XV_SHOW, TRUE, PANEL_CLIENT_DATA, &autoVaryRowElim, PANEL_VALUE, autoVaryRowElim, 0); (void) xv_create(autocontrolpanel, PANEL_SLIDER, PANEL_LABEL_STRING, "Max Height", PANEL_MIN_VALUE, 0, PANEL_MAX_VALUE, 10, PANEL_SHOW_RANGE, TRUE, PANEL_SHOW_VALUE, TRUE, PANEL_VALUE, MaxHeightWeight, PANEL_CLIENT_DATA, &MaxHeightWeight, PANEL_NOTIFY_PROC, PanelWeightProc, 0); (void) xv_create(autocontrolpanel, PANEL_SLIDER, PANEL_LABEL_STRING, "Lookahead Weight in tenths", PANEL_MIN_VALUE, 0, PANEL_MAX_VALUE, 10, PANEL_SHOW_RANGE, TRUE, PANEL_SHOW_VALUE, TRUE, PANEL_VALUE, LookAheadWeight, PANEL_CLIENT_DATA, &LookAheadWeight, PANEL_NOTIFY_PROC, PanelWeightProc, 0); (void) xv_create(autocontrolpanel, PANEL_SLIDER, PANEL_LABEL_STRING, "Holes before lookahead", PANEL_MIN_VALUE, 0, PANEL_MAX_VALUE, 10, PANEL_SHOW_RANGE, TRUE, PANEL_SHOW_VALUE, TRUE, PANEL_VALUE, HolesBeforeLookahead, PANEL_CLIENT_DATA, &HolesBeforeLookahead, PANEL_NOTIFY_PROC, PanelWeightProc, 0); time_item = xv_create(autocontrolpanel, PANEL_MESSAGE, PANEL_LABEL_STRING, "Time: 000 secs", 0); (void) xv_create(autocontrolpanel, PANEL_TOGGLE, PANEL_CHOICE_STRINGS, "Use seed", 0, PANEL_NOTIFY_PROC, PanelWeightProc, XV_SHOW, TRUE, PANEL_CLIENT_DATA, &useSeed, PANEL_VALUE, useSeed, 0); { char buf[32]; sprintf(buf, "%d", seed); seed_item = xv_create(autocontrolpanel, PANEL_TEXT, PANEL_LABEL_STRING, "Seed: ", PANEL_VALUE, buf, 0); } window_fit(autocontrolpanel); window_fit(autocontrolframe); } PanelWeightProc(item, event) Panel_item item; Event *event; { int *ptr; ptr = (int *)/* XView CONVERSION - You may wish to change this to xv_get format Look at panel.h and/or See Sect 2.5 */panel_get(item, PANEL_CLIENT_DATA); *ptr = (int)/* XView CONVERSION - You may wish to change this to xv_get format Look at panel.h and/or See Sect 2.5 */panel_get_value(item); show_score(); } /* variables available for choosing a play are: shape_no -- the current shape; rot -- the orientation of the current shape; next_no -- the next shape next_rot -- the next rotation currently next_no and next_rot are ignored, as is rot (since we have unlimited time to put the piece in any rotation we like). */ chooseplay() { int i; struct bestPlay play, selectPlay(); int bestrot, bestx; realclientData.count = 0; play = selectPlay(explainCanvasBuildCallback, &realclientData, 1 /* shortcut eval if possible */, displaygrid, display_shape_no, 0); bestx = play.bestx; bestrot = play.bestrot; while (display_rot != bestrot) { anti_proc(); } while (display_xpos > bestx) { left_proc(); } while (display_xpos < bestx) { right_proc(); } for (i = 0; i < 4; i++) { if (block_can_drop(display_shape_no, display_xpos, display_ypos, display_rot, displaygrid) == TRUE) { display_ypos += 1; } else { break; } } extra_work_after_selection(&play); freeRememberVals(); } /* * This selects a play. For each move considered, it calls the "callback" * function with the supplied clientdata, and a 'vals' structure. If * 'callback' is null, then no callback is done. */ struct bestPlay selectPlay(callback, clientData, truncate_eval, gridToEval, current_shape, lookahead) int (*callback)(); unsigned char gridToEval[UWIDTH][UHEIGHT]; { struct EvalContext contextrep; struct evalVals vals, bestvals; struct bestPlay returnval; int i; int newxpos, newrot, movescore, newypos; int besty = -200000000, bestx, bestrot, bestscore = -2000000000; if (autodump) printf("\n"); /* these are precomputations. "initNewgrid" is also called inside "evalPiece", and must be, but one extra call is cheap, compared to "evalHoles". */ copygrid(gridToEval, contextrep.grid); contextrep.lookaheaddone = 0; contextrep.lookaheaddone = lookahead; contextrep.shape_no = current_shape; contextrep.oldholecount = 0; contextrep.oldholecount = - evalHoles(&contextrep); /* negate because evalHoles returns its answer negatively */ contextrep.rememberedHoleY = firstHole(&contextrep); buildFullTable(&contextrep, contextrep.fullTable); /* auto vary the weight for row elim progress */ if (!lookahead && autoVaryRowElim) { int height; int oldweight1 = RowElimProgressWeight; int oldweight2 = RowElimWeight; int oldweight3 = HolesWeight; constructHeightTable(&contextrep); height = -evalMaxHeight(&contextrep); if (height > 10) { RowElimProgressWeight = 10; RowElimWeight = 10; HolesWeight = 20; } else { RowElimProgressWeight = 2; RowElimWeight = 1; HolesWeight = 40; } if (oldweight1 != RowElimProgressWeight) { xv_set(rowElimProgressItem, PANEL_VALUE, RowElimProgressWeight, 0); } if (oldweight2 != RowElimWeight) { xv_set(rowElimItem, PANEL_VALUE, RowElimWeight, 0); } if (oldweight3 != HolesWeight) { xv_set(holesItem, PANEL_VALUE, HolesWeight, 0); } } for (newrot = 0; newrot < shape[contextrep.shape_no].cycletop; newrot +=1) { for (newxpos = shape[contextrep.shape_no].minX[newrot]; newxpos <= shape[contextrep.shape_no].maxX[newrot]; newxpos += 1) { copygrid(gridToEval, contextrep.grid); newypos = getYpos(contextrep.shape_no, newrot, newxpos, &contextrep); vals = evalPiece(contextrep.shape_no, newrot, newxpos, newypos, truncate_eval ? bestscore : -2000000000, &contextrep); movescore = vals.totalscore; if (autodump) { printf("x=%d, y=%d, flat=%d, row=%d, hvar=%d, holes=%d, fiss=%d, exposeholes=%d, rowprog=%d, maxh=%d, end=%d, look=%d, tot=%d.\n", vals.xpos, vals.ypos, vals.maxflat, vals.rowelim, vals.variation, vals.holes, vals.fissure, vals.exposehole, vals.rowelimprogress, vals.maxheight, vals.gameover, vals.lookAhead, vals.totalscore); } if (callback && !contextrep.lookaheaddone) { (*callback)(clientData, &vals); } if (movescore > bestscore || (movescore == bestscore && ((newypos > besty && (vals.holes >= bestvals.holes)) || (vals.holes > bestvals.holes)))) { bestx = newxpos; bestrot = newrot; bestscore = movescore; besty = newypos; bcopy(&vals, &bestvals, sizeof(bestvals)); } } } returnval.bestx = bestx; returnval.bestrot = bestrot; returnval.bestscore = bestscore; return returnval; } /* Assumes newgrid has already been initialized. Return a value indicating how much height variation there is in the frontier if the shape is dropped. Height variation is: the difference between the lowest and highest positions on the frontier. */ evalVariation(context) Context context; { int x, returnval, min = 1000, max = -1; for (x = 0; x < UWIDTH; x++) { if (context->heightTable[x] > max) max = context->heightTable[x]; if (context->heightTable[x] < min) min = context->heightTable[x]; } returnval = max - min; /* return a negative, since variation is bad. */ if (returnval > 0) returnval = -returnval; return returnval; } /* Assumes newgrid has already been initialized. Return a value indicating how flat the frontier will be if the given shape is dropped. Flatness is defined to be: Let F(i) indicate the width of ith flat area. Then flatness is the sum, over all i, of (F(i)-1). For example, a frontier with no flats larger than 1 will have flatness 0. */ evalFlatness(context) Context context; { int x, flatness, currentFlat; flatness = 0; currentFlat = 0; for (x = 1; x < UWIDTH; x++) { if (context->heightTable[x-1] != context->heightTable[x] && currentFlat > 0) { flatness += currentFlat; currentFlat = 0; } else { currentFlat += 1; } } if (currentFlat > 0) { flatness += currentFlat; } return flatness - UWIDTH; /* ensure negative */ } /* Return a value measuring the number, and depth, of fissures in the frontier if the shape is dropped. A fissure is a hole of depth greater than a given depth on both sides. Wider fissures hurt less. */ evalFissures(context) Context context; { int returnval, fiss1, fiss2, fiss3, fiss4, fiss5; fiss1 = countFissuresOfWidthN(1, 2, context); fiss2 = countFissuresOfWidthN(2, 2, context); fiss3 = countFissuresOfWidthN(3, 3, context); fiss4 = countFissuresOfWidthN(4, 4, context); fiss5 = countFissuresOfWidthN(5, 4, context); returnval = fiss1 + fiss2 + fiss3 + fiss4 + fiss5; return returnval; } /* (note: height in the heightTable is inverted: 0 is at the top of playing field.) */ int countFissuresOfWidthN(n, depthfissure, context) Context context; int depthfissure; int n; { int x, width, maxheight, leftdepth, rightdepth, depth, returnval = 0; for (x = 0; x < UWIDTH-n+1; x++) { maxheight = context->heightTable[x]; for (width = 1; width < n; width += 1) { if (context->heightTable[x+width] < maxheight) /* "< is correct. see note." */ { maxheight = context->heightTable[x+width]; } } /* end of width loop */ if (x != 0) { /* not a left hand fissure */ leftdepth = maxheight - context->heightTable[x-1]; } if (x != UWIDTH-n) { /* not a right hand fissure */ rightdepth = maxheight - context->heightTable[x+n]; } if (x == 0 && rightdepth >= depthfissure) { returnval += (-rightdepth); } else if (x == UWIDTH-n && leftdepth >= depthfissure) { returnval += (-leftdepth); } else { depth = mymin(leftdepth, rightdepth); if (depth >= depthfissure) { returnval += (-depth); } } } /* end of x loop */ return returnval; } /* Return a value indicating total number of inaccessible holes on the grid if the shape is dropped. */ evalHoles(context) Context context; { int holecount = 0; int x, y; for (x = 0; x < UWIDTH; x += 1) { int startcount = 0; for (y = 0; y < UHEIGHT; y += 1) { if (context->grid[x][y] == 1) { startcount = 1; } else if (startcount) { holecount += 1; } } } return -holecount; } finishNewgrid(my_shape_no, xpos, ypos, rot, context) int my_shape_no, xpos, ypos, rot; Context context; { int i; for (i = 0; i < 4; i++) { if (shape[my_shape_no].table[i][rot] & 8) context->grid[xpos][ypos + i] = 1; if (shape[my_shape_no].table[i][rot] & 4) context->grid[xpos + 1][ypos + i] = 1; if (shape[my_shape_no].table[i][rot] & 2) context->grid[xpos + 2][ypos + i] = 1; if (shape[my_shape_no].table[i][rot] & 1) context->grid[xpos + 3][ypos + i] = 1; } } printTable(context) Context context; { int x; printf("ht = "); for (x = 0; x < UWIDTH; x++) { printf("%d ",context->heightTable[x]); } printf("\n"); } constructHeightTable(context) Context context; { int x, y, xcount; for (x = 0; x < UWIDTH; x++) { context->heightTable[x] = UHEIGHT; } xcount = 0; for (y = 0; y < UHEIGHT; y++) { for (x = 0; x < UWIDTH; x++) { if (context->heightTable[x] == UHEIGHT && context->grid[x][y] == 1) { context->heightTable[x] = y; xcount += 1; } } if (xcount >= UWIDTH) break; } } /* Assumes newgrid has already been initialized. Evaluates whether or not the move will end the game. If so, returns -100, else 0. */ evalGameOver(newypos, context) Context context; { if (newypos < 0) { return -100; } else { return 0; } } /* Assumes newgrid has already been initialized. Return a value indicating how many rows get wiped out if the given shape is dropped at the given position. */ evalRows(context) Context context; { int x, y, allempty, allfull, returnval; returnval = 0; for (y = UHEIGHT-1; y >= 0; y--) { allempty = 1; allfull = 0; for (x = 0; x < UWIDTH; x++) { if (context->grid[x][y] == 1) { allempty = 0; allfull += 1; } } if (allfull == UWIDTH) { returnval += 1; } if (allempty == 1) break; } return returnval - UHEIGHT; /* always negative */ } /* * Return a Y value which is the first row with a hole in grid. */ firstHole(context) Context context; { int pieceSeenAtX[UWIDTH]; int X, holeY; bzero(pieceSeenAtX, sizeof(pieceSeenAtX)); /* find the row with the highest holes */ for (holeY = 0; holeY < UHEIGHT; holeY += 1) { for (X = 0; X < UWIDTH; X += 1) { if (context->grid[X][holeY] == 1 && !pieceSeenAtX[X]) { pieceSeenAtX[X] = 1; } else { if (context->grid[X][holeY] == 0 && pieceSeenAtX[X]) { goto done; } } } /* end of X loop */ } /* end of holeY loop */ done: return holeY; } /* * Compute how far each row is from being full. */ buildFullTable(context, table) Context context; int table[UHEIGHT]; { register int x, y, fullness, allempty; allempty = 0; for (y = UHEIGHT-1; y >= 0; y -= 1) { fullness = 0; if (allempty == 0) { /* still serious work to be done */ allempty = 1; for (x = 0; x < UWIDTH; x += 1) { if (context->grid[x][y] == 1) { fullness += 1; allempty = 0; } } } table[y] = fullness; } } /* the first position is '0' so we don't double count filling a row */ #define fullnessTableWidth 4 #define MAXFULLNESSWEIGHT 4 static fullnessweights[fullnessTableWidth] = {0, 4, 2, 1}; /* * If this move gets us closer to eliminating a row, return how much closer * we are, weighted by being almost full... */ evalRowProgress(context) Context context; { int localTable[UHEIGHT]; int x, y, allempty, allfull, returnval; returnval = 0; buildFullTable(context, localTable); for (y = 0; y < UHEIGHT; y += 1) { if ((localTable[y] - context->fullTable[y])) { if ((UWIDTH - localTable[y]) < fullnessTableWidth) { returnval += fullnessweights[UWIDTH - localTable[y]]; } } } return returnval/2 - (MAXFULLNESSWEIGHT*UHEIGHT); /* divide by two so it does not overwhelm */ } /* * Return a value which is more negative the more pieces are on top of * the highest holes. */ evalExposeHoles(context) Context context; { int X, holeY, Y, returnval; holeY = context->rememberedHoleY; if (holeY >= UHEIGHT) { /* no holes */ return 0; } else { returnval = 0; /* find the height above all the holes in this row */ for (X = 0; X < UWIDTH; X += 1) { if (context->grid[X][holeY] == 1) { /* not a hole here, skip */ continue; } for (Y = 0; Y < holeY; Y += 1) { if (context->grid[X][Y] == 1) { /* found the top of this column */ returnval += holeY - Y; break; } } /* end of Y loop */ } /* end of X loop */ } /* end of "no holes" IF-statment */ return -returnval; } evalMaxHeight(context) Context context; { int x, max; max = UHEIGHT; for (x = 0; x < UWIDTH; x += 1) { if (context->heightTable[x] < max) { /* height table is inverted, with 0 at the top */ max = context->heightTable[x]; } } return max - UHEIGHT; /* the higher, the more negative */ } internalError() { printf("Value > 0 returned from eval func.\n"); abort(); } static struct evalVals evalPiece(my_shape_no, rot, newxpos, newypos, bestsofar, context) int my_shape_no, rot, newxpos, newypos; Context context; { struct evalVals vals; int movescore; finishNewgrid(my_shape_no, newxpos, newypos, rot, context); constructHeightTable(context); /* newgrid now contains the new piece, ready for use */ /* first do the eval for functions that want to see the board before row elimination */ #define INCRTOTALSCORE(x) {vals.totalscore += vals.x; if (vals.totalscore < bestsofar) goto end; } vals.totalscore = 0; if (RowElimWeight) {vals.rowelim = evalRows(context) * RowElimWeight; INCRTOTALSCORE(rowelim); } if (GameOverWeight) {vals.gameover = evalGameOver(newypos, context) * GameOverWeight; INCRTOTALSCORE(gameover); } /* Now eliminate rows, and do the remaining functions. */ remove_full_lines_special(context->grid, newypos); constructHeightTable(context); if (HolesWeight) { vals.holes = evalHoles(context) * HolesWeight; INCRTOTALSCORE(holes); } if (MaxFlatWeight) { vals.maxflat = evalFlatness(context) * MaxFlatWeight; INCRTOTALSCORE(maxflat); } if (VariationWeight) { vals.variation = evalVariation(context) * VariationWeight; INCRTOTALSCORE(variation); } if (FissureWeight) { vals.fissure = evalFissures(context) * FissureWeight; INCRTOTALSCORE(fissure); } if (ExposeHoleWeight) { vals.exposehole = evalExposeHoles(context) * ExposeHoleWeight; INCRTOTALSCORE(exposehole); } if (MaxHeightWeight) { vals.maxheight = evalMaxHeight(context) * MaxHeightWeight; INCRTOTALSCORE(maxheight); } if (RowElimProgressWeight) { vals.rowelimprogress = evalRowProgress(context) * RowElimProgressWeight; INCRTOTALSCORE(rowelimprogress); } if (LookAheadWeight) { vals.lookAhead = evalLookAhead(context) * LookAheadWeight / 10; INCRTOTALSCORE(lookAhead); } end: vals.xpos = newxpos; vals.ypos = newypos; vals.rot = rot; return vals; } int getYpos(my_shape_no, rot, newxpos, context) int my_shape_no, rot, newxpos; Context context; { int mynewypos; mynewypos = display_ypos; while (block_can_drop(my_shape_no, newxpos, mynewypos, rot, context->grid) == TRUE) { mynewypos += 1; } return mynewypos; } mymin(x, y) { return x < y ? x : y; } #define EXPLAINHEIGHT 16 #define TINYSQUARE 4 #define HEIGHTGAP 2 #define MAXCANVAS 64 static struct evalVals *rememberVals[UWIDTH*4]; explainCanvasBuildCallback(clientdata, vals) struct explainclientData *clientdata; struct evalVals *vals; { if (clientdata->count > MAXCANVAS) { return; } rememberVals[clientdata->count] = (struct evalVals *)malloc(sizeof(*vals)); bcopy(vals, rememberVals[clientdata->count], sizeof(*vals)); clientdata->count += 1; } evalValsCompare(v1, v2) struct evalVals **v1, **v2; { return (*v2)->totalscore - (*v1)->totalscore; } explainCanvasRepaintProc(canvas, pw, repaint_area) Canvas canvas; Pixwin *pw; Rectlist *repaint_area; { draw_explain(pw); } draw_explain(pw) Pixwin *pw; { struct evalVals *vals; Server_image pr; int valsptr; int priorY = 0; int textadjust; char *buff[256]; pr = xv_create(XV_NULL, SERVER_IMAGE, XV_HEIGHT, TINYSQUARE*5, XV_WIDTH, TINYSQUARE*4, 0); qsort((char *)&rememberVals[0], realclientData.count, sizeof(rememberVals[0]), evalValsCompare); for (valsptr = 0; valsptr < realclientData.count; valsptr += 1) { textadjust = 0; vals = rememberVals[valsptr]; buildTinyPr(pr, display_shape_no, vals->rot, TINYSQUARE, TINYSQUARE, TINYSQUARE); pw_rop(pw, 0, priorY, TINYSQUARE*5, TINYSQUARE*4, PIX_SRC, pr, 0, 0); if (vals->xpos == display_xpos && vals->rot == display_rot) { pw_text(pw, 5*TINYSQUARE+1, priorY + EXPLAINHEIGHT/2, PIX_SRC | PIX_COLOR(BLACK), tinyfont, "*"); } sprintf(buff, "x=%d, y=%d, flat=%d, row=%d, hvar=%d, holes=%d, fiss=%d, exposeholes=%d, rowprog=%d, maxh=%d, end=%d, look=%d, tot=%d.\n", vals->xpos, vals->ypos, vals->maxflat, vals->rowelim, vals->variation, vals->holes, vals->fissure, vals->exposehole, vals->rowelimprogress, vals->maxheight, vals->gameover, vals->lookAhead, vals->totalscore); pw_text(pw, textadjust + 7*TINYSQUARE + TINYSQUARE/2, priorY + EXPLAINHEIGHT/2, PIX_SRC | PIX_COLOR(BLACK), tinyfont, buff); priorY += EXPLAINHEIGHT+HEIGHTGAP; } } /* The actual pixrect built will have subsquares of size 'size' pixels, * and so may be up to 4*size pixels on a side. */ buildTinyPr(pr, tiny_shape_no, rot, size, xoffset, yoffset) Server_image pr; int tiny_shape_no; int xoffset, yoffset; { int word, ystep, xstep; if (color) { pw_writebackground(pr, 0, 0, xv_get(pr, WIN_WIDTH), xv_get(pr, WIN_HEIGHT), PIX_CLR | PIX_COLOR(GRAY)); } else { pw_rop(pr, 0, 0, xv_get(pr, WIN_WIDTH), xv_get(pr, WIN_HEIGHT), PIX_SRC, 0, 0, 0); } for (ystep = 0; ystep < 4; ystep += 1) { word = shape[tiny_shape_no].table[ystep][rot]; for (xstep = 0; xstep < 4; xstep += 1) { if (word & 0x8) { pw_rop(pr, xoffset + (xstep*size), yoffset + (ystep*size), size, size, PIX_SRC | PIX_COLOR(shape[tiny_shape_no].color), 0, 0, 0); } word <<= 1; } } } explainProc() { int i; Pixwin *explainpw, *winpw; Panel explainPanel; pause_proc(); explainFrame = xv_create(mainframe, FRAME, FRAME_SHOW_LABEL, TRUE, XV_LABEL, "Explanation of Auto Tetris Move - weiser", WIN_SHOW, FALSE, 0); explainPanel = xv_create(explainFrame, PANEL, 0); xv_create(explainPanel, PANEL_BUTTON, PANEL_LABEL_IMAGE, panel_button_image(explainPanel, "Done", 4, 0), PANEL_NOTIFY_PROC, explainDoneProc, 0); window_fit(explainPanel); explainCanvas = xv_create(explainFrame, CANVAS, XV_WIDTH, 830, XV_HEIGHT, (EXPLAINHEIGHT+HEIGHTGAP)*MAXCANVAS, WIN_X, 0, WIN_BELOW, explainPanel, /* XView CONVERSION - Now only a hint, must be prepared to repaint, read Sect 2.5 */CANVAS_RETAINED, FALSE, CANVAS_REPAINT_PROC, explainCanvasRepaintProc, OPENWIN_AUTO_CLEAR, TRUE, /* WIN_DYNAMIC_VISUAL, TRUE, */ 0); explainpw = canvas_pixwin(explainCanvas); setup_colours(explainpw); realclientData.count = 0; selectPlay(explainCanvasBuildCallback, &realclientData, 0 /* don't truncate evaluation */, displaygrid, 0, 0); xv_set(explainCanvas, XV_HEIGHT, (EXPLAINHEIGHT+HEIGHTGAP)*realclientData.count, 0); window_fit(explainFrame); xv_set(explainFrame, WIN_SHOW, TRUE, 0); } explainDoneProc() { xv_set(explainFrame, FRAME_NO_CONFIRM, TRUE, 0); xv_destroy_safe(explainFrame); freeRememberVals(); } catch_aux(aux_table) struct aux_score_table *aux_table; { aux_table->version = MYVERSION; aux_table->seed = seed; aux_table->RowElimWeight = RowElimWeight; aux_table->MaxFlatWeight = MaxFlatWeight; aux_table->VariationWeight = VariationWeight; aux_table->HolesWeight = HolesWeight; aux_table->GameOverWeight = GameOverWeight; aux_table->FissureWeight = FissureWeight; aux_table->ExposeHoleWeight = ExposeHoleWeight; aux_table->RowElimProgressWeight = RowElimProgressWeight; aux_table->MaxHeightWeight = MaxHeightWeight; aux_table->Seconds = (((int) time((time_t *) 0)) - starttime); aux_table->LookAheadWeight = LookAheadWeight; } remove_full_lines_special(mygrid, ystart) unsigned char mygrid[UWIDTH][UHEIGHT]; { register int y1, y2, x, full_flag; for (y1 = ystart; y1 < ystart + 4 && y1 < UHEIGHT; y1 += 1) { full_flag = TRUE; for (x = 0; x < UWIDTH; x += 1) { if (mygrid[x][y1] == 0) { full_flag = FALSE; break; } } if (full_flag) { for (y2 = y1; y2 > 0; y2 -= 1) { for (x = 0; x < UWIDTH; x += 1) { mygrid[x][y2] = mygrid[x][y2-1]; } } for (x = 0; x < UWIDTH; x++) { mygrid[x][0] = 0; } } } } freeRememberVals() { int valsptr; for (valsptr = 0; valsptr < realclientData.count; valsptr += 1) { free(rememberVals[valsptr]); } } extra_work_after_selection(play) struct bestPlay *play; { int valsptr; for (valsptr = 0; valsptr < realclientData.count; valsptr += 1) { if (play->bestx == rememberVals[valsptr]->xpos && play->bestrot == rememberVals[valsptr]->rot) { if (rememberVals[valsptr]->holes < 0 && explainholes) { do_with_delay(explainProc, 0, 1); break; /* essential to break, since 'explainProc' destroys structure */ } } } } /* * Call procedure f in a little while. */ struct call_wrapper { /* Dynamically allocating a wrapper ensures unique notifier id's. */ void (*f)(); }; do_with_delay(f, secs, usecs) void (*f)(); int secs, usecs; { Notify_value do_the_call(); struct call_wrapper *w; struct itimerval timer; /* Sigh, so much work just to wait a bit before starting up. */ timer.it_interval.tv_usec = 0; timer.it_interval.tv_sec = 0; timer.it_value.tv_usec = usecs; timer.it_value.tv_sec = secs; w = (struct call_wrapper *)calloc(sizeof(struct call_wrapper), 1); w->f = f; notify_set_itimer_func(w, do_the_call, ITIMER_REAL, &timer, NULL); } /* * Wrapper to make sure procedures from do_with_delay return good values * to the notifier. */ Notify_value do_the_call(w, which) struct call_wrapper *w; { (*(w->f))(); free(w); return NOTIFY_DONE; } void control_panel_proc() { xv_set(autocontrolframe, WIN_SHOW, TRUE, 0); } /* Only works for one level of lookahead */ evalLookAhead(context) Context context; { struct bestPlay b; if (context->lookaheaddone == 0 && context->oldholecount >= HolesBeforeLookahead) { /* window_update(); */ b = selectPlay(0 /* no call back */, 0 /* no client data */, 1 /* truncate eval */, context->grid, next_no, 1); return b.bestscore; } else { return 0; } } copygrid(oldgrid, newgrid) unsigned int newgrid; unsigned int oldgrid; { bcopy(oldgrid, newgrid, sizeof(char)*UWIDTH*UHEIGHT); } window_update_timed(secs) { int width = 0; fd_set readfds, writefds, exceptfds; struct timeval real_timeout; FD_ZERO(&writefds); FD_ZERO(&readfds); FD_ZERO(&exceptfds); real_timeout.tv_sec = secs; real_timeout.tv_usec = 0; select(width, &readfds, &writefds, &exceptfds, &real_timeout); } window_update() { window_update_timed(0); }