/* * Nonie's Dots-and-Boxes * Written by Arnon Politi */ #include "dots.h" int board[SIZEY][SIZEX]; int compboard[SIZEY][SIZEX]; prmove goodmoves[MAXMOVES]; int main(void) { int reset; int currentplayer; xymove move; while (1) { reset = 0; initialize(); currentplayer = *Timer % 2; display_arrow(currentplayer); while (TOTALSCORE < TOTALBOXES){ move = get_move(currentplayer); if (move.x == -1) { reset = 1; break; } board[move.y][move.x] = LINE; if (complete_boxes(move, currentplayer)) { display_score(); } else { currentplayer = currentplayer ^ 1; display_arrow(currentplayer); } } if (reset) continue; if (player[0].score > player[1].score) { roll_message(WIN_MSG); } else if (player[0].score < player[1].score) { roll_message(LOSE_MSG); } else { roll_message(TIE_MSG); } } return 0; } void initialize(void) { int i, j; volatile byte *theport; byte thebit; for (i = 0; i < NUM_PORTS; i++) { *(port[i].control) = 0xFF; } *(SWPort.control) = 0xFF; for (i = 0; i < NUM_PORTS; i++) { *(port[i].data) = 0xFF; *(port[i].control) = 0x00; } for (i = 0; i < SIZEY; i++) for (j = 0; j < SIZEX; j++) board[i][j] = EMPTY; player[0].score = player[1].score = 0; for (i = 0; i < LCD_CHARS; i++) { lcdwrite((LCD_SET_CGRAM | i << 3), LCD_IR); for (j = 0; j < 8; j++) { lcdwrite(LCD_CGRAM[i][j], LCD_DR); } } lcdwrite(0x0C, LCD_IR); // Display ON, Cursor OFF, Blink OFF. lcdwrite(0x06, LCD_IR); // Increment address mode. lcdwrite(0x01, LCD_IR); // Clear screen. for (i = 0; i < ROWS; i++) { lcdwrite((LCD_SET_DDRAM | LCD_LINE_ADR[i]), LCD_IR); for (j = 0; j < COLS; j++) { lcdwrite(0x00, LCD_DR); lcdwrite(0x01, LCD_DR); } } lcdwrite((LCD_SET_DDRAM | (LCD_LINE_ADR[1] + 9)), LCD_IR); for (i = 0; i < 4; i++) { lcdwrite(player[0].NAME[i], LCD_DR); } lcdwrite((LCD_SET_DDRAM | (LCD_LINE_ADR[1] + 16)), LCD_IR); for (i = 0; i < 4; i++) { lcdwrite(player[1].NAME[i], LCD_DR); } display_score(); delay(RESET_DELAY); for (i = 0; i < SIZEY; i += 2) { for (j = 1; j < SIZEX; j += 2) { theport = port[PORTMAP[i][j].PORT].data; thebit = (1 << PORTMAP[i][j].BIT); *theport = *theport & ~thebit; } delay(ANIM_DELAY); } for (i = 0; i < SIZEX; i += 2) { for (j = 1; j < SIZEY; j += 2) { theport = port[PORTMAP[j][i].PORT].data; thebit = (1 << PORTMAP[j][i].BIT); *theport = *theport & ~thebit; } delay(ANIM_DELAY); } } xymove get_move(int playernum) { const xymove RESET = {-1, -1}; xymove move = {0, 1}; volatile byte *theport; byte thebit; byte poll, pollbits; int lowest = PRIO_MAX; int i, j; int numberofmoves, themove; if (playernum == 0) { poll = delay_poll(PLAY_DELAY, SW_RESET); if (poll == SW_RESET) { return RESET; } while (1) { theport = port[PORTMAP[move.y][move.x].PORT].data; thebit = (1 << PORTMAP[move.y][move.x].BIT); *theport = (*theport ^ thebit); pollbits = (board[move.y][move.x] == EMPTY) ? (SW_ALL) : (SW_ALL & ~SW_SET); while (*(SWPort.data) & pollbits); poll = delay_poll(FLASH_DELAY, pollbits); if (poll) { if (board[move.y][move.x] == EMPTY) { *theport = *theport & ~thebit; } else { *theport = *theport | thebit; } } switch (poll) { case SW_LEFT: move.x = (move.x - 2 + (SIZEX + ((move.y % 2) ? 1 : -1))) % (SIZEX + ((move.y % 2) ? 1 : -1)); break; case SW_RIGHT: move.x = (move.x + 2) % (SIZEX + ((move.y % 2) ? 1 : -1)); break; case SW_UP: move.y = (move.y - 2 + (SIZEY + ((move.x % 2) ? 1 : -1))) % (SIZEY + ((move.x % 2) ? 1 : -1)); break; case SW_DOWN: move.y = (move.y + 2) % (SIZEY + ((move.x % 2) ? 1 : -1)); break; case SW_ROTATE: move.x = move.y % 2; move.y = move.x ^ 1; break; case SW_SET: *theport = *theport | thebit; return move; break; case SW_RESET: return RESET; break; } } } else { init_comp(); numberofmoves = long_chains(); if (!numberofmoves) { numberofmoves = closed_four_chains(); if (!numberofmoves) { numberofmoves = two_chains(); if (!numberofmoves) { numberofmoves = any_good_move(); } } } for (i = 0; i < numberofmoves; i++) { if (goodmoves[i].priority < lowest) { lowest = goodmoves[i].priority; } } for (i = 0, j = 0; i < numberofmoves; i++) { if (goodmoves[i].priority == lowest) { goodmoves[j].x = goodmoves[i].x; goodmoves[j].y = goodmoves[i].y; goodmoves[j].priority = goodmoves[i].priority; j++; } } themove = mod((int) *Timer, j); move.x = goodmoves[themove].x; move.y = goodmoves[themove].y; poll = delay_poll(PLAY_DELAY, SW_RESET); if (poll == SW_RESET) { return RESET; } theport = port[PORTMAP[move.y][move.x].PORT].data; thebit = (1 << PORTMAP[move.y][move.x].BIT); for (i = 0; i < (COMP_FLASHES * 2 + 1); i++) { poll = delay_poll(FLASH_DELAY, SW_RESET); if (poll == SW_RESET) { return RESET; } *theport = (*theport ^ thebit); } return move; } return RESET; // Makes the compiler happy } int mod(int op1, int op2) { while (op1 >= op2) { op1 = op1 - op2; } return op1; } int complete_boxes(xymove move, int playernum) { int boxes = 0; if (move.y % 2) { if (move.x != 0) boxes += check_box(move.y, move.x - 1, playernum); if (move.x != SIZEX - 1) boxes += check_box(move.y, move.x + 1, playernum); } else { if (move.y != 0) boxes += check_box(move.y - 1, move.x, playernum); if (move.y != SIZEY - 1) boxes += check_box(move.y + 1, move.x, playernum); } return boxes; } int check_box(int y, int x, int playernum) { if ( (board[y - 1][x] == LINE) && (board[y][x + 1] == LINE) && (board[y + 1][x] == LINE) && (board[y][x - 1] == LINE) ) { board[y][x] = playernum; display_box(y, x, playernum); player[playernum].score++; return 1; } return 0; } void lcdwrite(byte data, const byte RS) { delay(LCD_DELAY); *portB = RS; *portA = data; *portB = *portB ^ LCD_EN; *portB = *portB ^ LCD_EN; } void display_score(void) { lcdwrite((LCD_SET_DDRAM | (LCD_LINE_ADR[2] + 10)), LCD_IR); lcdwrite((((player[0].score / 10) % 10) + '0'), LCD_DR); lcdwrite((((player[0].score / 1) % 10) + '0'), LCD_DR); lcdwrite((LCD_SET_DDRAM | (LCD_LINE_ADR[2] + 17)), LCD_IR); lcdwrite((((player[1].score / 10) % 10) + '0'), LCD_DR); lcdwrite((((player[1].score / 1) % 10) + '0'), LCD_DR); } void display_box(int y, int x, int playernum) { lcdwrite((LCD_SET_DDRAM | (LCD_LINE_ADR[(y - 1)/2] + (x - 1))), LCD_IR); lcdwrite(0x02 + (2 * playernum), LCD_DR); lcdwrite(0x03 + (2 * playernum), LCD_DR); } void display_arrow(int playernum) { lcdwrite((LCD_SET_DDRAM | (LCD_LINE_ADR[2] + 13)), LCD_IR); if (playernum == 0) { lcdwrite(0x7F, LCD_DR); lcdwrite(0x20, LCD_DR); lcdwrite(0x20, LCD_DR); } else { lcdwrite(0x20, LCD_DR); lcdwrite(0x20, LCD_DR); lcdwrite(0x7E, LCD_DR); } } void roll_message(const char MSG[]) { int len; int i, j; while (*(SWPort.data) & SW_ALL); for (len = 0; MSG[len] != '\0'; len++); for (i = 0; ; i = (mod((i + 1), len))) { lcdwrite((LCD_SET_DDRAM | (LCD_LINE_ADR[3] + 9)), LCD_IR); lcdwrite('[', LCD_DR); for (j = 0; j < 9; j++) { lcdwrite(MSG[mod((i + j), len)], LCD_DR); } lcdwrite(']', LCD_DR); if (delay_poll(ROLL_DELAY, SW_ALL)) { break; } } } byte delay_poll(int msec, byte pollbits) { byte poll, tmptimer; int counter; for (counter = 0; counter < msec; counter++) { if ((poll = (*(SWPort.data) & pollbits))) { delay(BOUNCE_DELAY); return poll; } tmptimer = *Timer; while (*Timer == tmptimer); } return 0x00; } void delay(int msec) { byte tmptimer; int counter; for (counter = 0; counter < msec; counter++) { tmptimer = *Timer; while (*Timer == tmptimer); } } void init_comp(void) { int i, j; for (i = 0; i < SIZEY; i++) for (j = 0; j < SIZEX; j++) compboard[i][j] = board[i][j]; for (i = 0; i < MAXMOVES; i++) { goodmoves[i].x = goodmoves[i].y = EMPTY; goodmoves[i].priority = PRIO_MAX; } } int long_chains(void) { int y, x; int count; int isclosed; int moveindex = 0; for (y = 1; y < SIZEY; y += 2) for (x = 1; x < SIZEX; x += 2) if (lines_around(y, x) == 3) { count = count_chain(y, x, &isclosed); clear_chainmarks(); if (count == 2 && isclosed) { add_missing_line(&moveindex, y, x, 1); } else if (count == 1) { add_missing_line(&moveindex, y, x, 2); } else if ((count == 4 && isclosed) || count == 2) { /* Do nothing for now */ } else { add_missing_line(&moveindex, y, x, 3); } } return moveindex; } int closed_four_chains(void) { int y, x; int nexty, nextx; int firsty, firstx, middley, middlex; int count; int isclosed; int mincount; int moveindex = 0; for (y = 1; y < SIZEY; y += 2) { for (x = 1; x < SIZEX; x += 2) { if (lines_around(y, x) == 3) { count = count_chain(y, x, &isclosed); clear_chainmarks(); if (count == 4 && isclosed) { nexty = y; nextx = x; next_in_chain(&nexty, &nextx); firsty = (y + nexty) / 2; firstx = (x + nextx) / 2; compboard[firsty][firstx] = LINE; y = nexty; x = nextx; next_in_chain(&nexty, &nextx); middley = (y + nexty) / 2; middlex = (x + nextx) / 2; compboard[nexty - 1][nextx] = LINE; compboard[nexty + 1][nextx] = LINE; compboard[nexty][nextx - 1] = LINE; compboard[nexty][nextx + 1] = LINE; mincount = min_count(); if (mincount <= 2 || mincount == PRIO_MAX) { add_move(&moveindex, firsty, firstx, 0); return moveindex; } else { add_move(&moveindex, middley, middlex, 0); return moveindex; } } } } } return 0; } int two_chains(void) { int y, x; int nexty, nextx; int middley, middlex, endy, endx; int count; int isclosed; int mincount; int moveindex = 0; for (y = 1; y < SIZEY; y += 2) { for (x = 1; x < SIZEX; x += 2) { if (lines_around(y, x) == 3) { count = count_chain(y, x, &isclosed); clear_chainmarks(); if (count == 2) { nexty = y; nextx = x; next_in_chain(&nexty, &nextx); middley = (y + nexty) / 2; middlex = (x + nextx) / 2; compboard[middley][middlex] = LINE; y = nexty; x = nextx; if (compboard[y - 1][x] != LINE) { endy = y - 1; endx = x; } else if (compboard[y][x - 1] != LINE) { endy = y; endx = x - 1; } else if (compboard[y + 1][x] != LINE) { endy = y + 1; endx = x; } else { endy = y; endx = x + 1; } compboard[endy][endx] = LINE; mincount = min_count(); if (mincount <= 2 || mincount == PRIO_MAX) { add_move(&moveindex, middley, middlex, 0); return moveindex; } else { add_move(&moveindex, endy, endx, 0); return moveindex; } } } } } return 0; } int any_good_move(void) { int y, x; int length; int count1, count2; int priority; int moveindex = 0; for (y = 0; y < SIZEY; y++) for (x = !(y % 2); x < SIZEX; x += 2) if (compboard[y][x] != LINE) { length = move_chain_length(y, x, &count1, &count2); if (length < 2) { priority = length - 1; } else if (count1 == 1 && count2 == 1) { priority = 1; } else { priority = length; } add_move(&moveindex, y, x, priority); } return moveindex; } int count_chain(int nexty, int nextx, int *isclosed) { int count; *isclosed = 0; if (compboard[nexty][nextx] == CHAINMARK) return 0; for (count = 1; ; count++) { compboard[nexty][nextx] = CHAINMARK; next_in_chain(&nexty, &nextx); if (nexty == CLOSED) { *isclosed = 1; break; } if (nexty == EDGE || lines_around(nexty, nextx) < 2) { break; } } return count; } void next_in_chain(int *nexty, int *nextx) { int y = *nexty; int x = *nextx; if (compboard[y - 1][x] != LINE) { if (y == 1) { *nexty = EDGE; *nextx = EDGE; return; } else { if (compboard[y - 2][x] != CHAINMARK) { *nexty = y - 2; *nextx = x; return; } } } if (compboard[y][x - 1] != LINE) { if (x == 1) { *nexty = EDGE; *nextx = EDGE; return; } else { if (compboard[y][x - 2] != CHAINMARK) { *nexty = y; *nextx = x - 2; return; } } } if (compboard[y + 1][x] != LINE) { if (y == SIZEY - 2) { *nexty = EDGE; *nextx = EDGE; return; } else { if (compboard[y + 2][x] != CHAINMARK) { *nexty = y + 2; *nextx = x; return; } } } if (compboard[y][x + 1] != LINE) { if (x == SIZEX - 2) { *nexty = EDGE; *nextx = EDGE; return; } else { if (compboard[y][x + 2] != CHAINMARK) { *nexty = y; *nextx = x + 2; return; } } } *nexty = CLOSED; *nextx = CLOSED; } int lines_around(int y, int x) { return ((compboard[y - 1][x] == LINE) ? 1 : 0) + ((compboard[y][x + 1] == LINE) ? 1 : 0) + ((compboard[y + 1][x] == LINE) ? 1 : 0) + ((compboard[y][x - 1] == LINE) ? 1 : 0); } void clear_chainmarks(void) { int y, x; for (y = 1; y < SIZEY; y += 2) for (x = 1; x < SIZEX; x += 2) compboard[y][x] = EMPTY; } void add_missing_line(int *moveindex, int y, int x, int priority) { if (compboard[y - 1][x] != LINE) add_move(moveindex, y - 1, x, priority); else if (compboard[y][x - 1] != LINE) add_move(moveindex, y, x - 1, priority); else if (compboard[y + 1][x] != LINE) add_move(moveindex, y + 1, x, priority); else add_move(moveindex, y, x + 1, priority); } void add_move(int *moveindex, int y, int x, int priority) { goodmoves[*moveindex].y = y; goodmoves[*moveindex].x = x; goodmoves[*moveindex].priority = priority; (*moveindex)++; } int min_count(void) { int y, x; int count; int c1, c2; int mincount = PRIO_MAX; for (y = 0; y < SIZEY; y++) for (x = ((y % 2) ^ 1); x < SIZEX; x += 2) if (compboard[y][x] != LINE) { count = move_chain_length(y, x, &c1, &c2); if (count < mincount) mincount = count; } return mincount; } int move_chain_length(int y, int x, int *c1, int *c2) { int count1 = 0; int count2 = 0; int isclosed; compboard[y][x] = LINE; if (y % 2) { if (x != 0) if (lines_around(y, x - 1) == 3) count1 = count_chain(y, x - 1, &isclosed); if (x != SIZEX - 1) if (lines_around(y, x + 1) == 3) count2 = count_chain(y, x + 1, &isclosed); } else { if (y != 0) if (lines_around(y - 1, x) == 3) count1 = count_chain(y - 1, x, &isclosed); if (y != SIZEY - 1) if (lines_around(y + 1, x) == 3) count2 = count_chain(y + 1, x, &isclosed); } compboard[y][x] = EMPTY; clear_chainmarks(); *c1 = count1; *c2 = count2; return count1 + count2; }