//SNAKE PARA MSP430 - VERSÃO FINAL
//MODELO programado em C está publicado no post anterior
//Vídeos explicativos nos links abaixo
//
Parte 1
//
Parte 2
//
Parte 3
//JOGO SNAKE
#include
#include #include //Define o valor da constante RAND_MAX -> rand() produz um valor entre 0 e RAND_MAX #include //Permite o uso da função rand() #include //______________________________________________________________________________ //DECLARAÇÃO DAS VARIÁVEIS GLOBAIS unsigned int matriz[8][8]; //tabuleiro unsigned int *zeros[16],*snake[36],*cresce[3]; //vetores de ponteiros unsigned int i,j,scorep,cont,movimento,level; unsigned int contazeros,size,lugar,headline,headcol; //variáveis de operação //______________________________________________________________________________ //DECLARAÇÃO DE FUNÇÕES //ENCONTRA ESPAÇO VAZIO E GERA NOVO PONTO void gerafruta(void) //Procura espaços vazios na matriz { contazeros=0; lugar = rand()%4; //gera um valor aleatório para procura em 1 dos 4 quadrantes if (lugar==2) //1o quadrante { for (i=0;i<4 br="" i=""> { for(j=0;j<4 br="" j=""> { if (matriz[i][j]==0) //Encontra os espaços vazios na matriz { zeros[contazeros] = &matriz[i][j]; contazeros++; } } } } else if (lugar==3 || contazeros==0) //2o quadrante { for (i=0;i<4 br="" i=""> { for(j=4;j<8 br="" j=""> { if (matriz[i][j]==0) //Encontra os espaços vazios na matriz { zeros[contazeros] = &matriz[i][j]; contazeros++; } } } } else if (lugar==1 || contazeros==0) //3o quadrante { for (i=4;i<8 br="" i=""> { for(j=0;j<4 br="" j=""> { if (matriz[i][j]==0) //Encontra os espaços vazios na matriz { zeros[contazeros] = &matriz[i][j]; contazeros++; } } } } else if (lugar==0 || contazeros==0) //4o quadrante { for (i=4;i<8 br="" i=""> { for(j=4;j<8 br="" j=""> { if (matriz[i][j]==0) //Encontra os espaços vazios na matriz { zeros[contazeros] = &matriz[i][j]; contazeros++; } } } } lugar = rand()%contazeros; //gera um valor aleatório (0 a contazeros-1) *zeros[lugar] = 2; //zeros[lugar] = endereço da matriz onde foi criada a nova fruta /*Na funçao acima, foi criado um vetor de ponteiros. No vetor zeros[] são armazenados os endereços de memoria dos zeros da matriz A função rand() gera um número entre zero e RAND_MAX (definido na biblioteca stdlib.h, com o valor de 0x3fffffff no caso desse programa) e o operador % devolve o resto da divisão (para um valor x temos x%3 = 0, 1 ou 2) A variável lugar gera um número aleatorio dentre as posiçoes do vetor de zeros Por fim o LED correspondente ao espaço da matriz é ligado Tendo ocorrido problemas de falta de memória para armazenar vetores de 64 posições o vetor de zeros foi reduzido para 16 posições, e faz referência a um dos quatro quadrantes escolhidos aleatóriamente para busca de um espaço vazio que será preenchido pela nova fruta */ } void reinicia(void) { //INICIALIZA MATRIZ VETORES E VARIAVEIS i=0;j=0;scorep=0;cont=0;contazeros=0;lugar=0;size=0;headline=0;headcol=0;movimento=0; for (i=0;i<36 br="" i="" snake=""> for (i=0;i<16 br="" i="" zeros=""> for (i=0;i<3 br="" cresce="" i=""> for (i=0;i<8 br="" i="" inicializa="" matriz=""> { for (j=0;j<8 br="" j=""> { matriz[i][j]=0; } } //SITUAÇÃO INICIAL DO JOGO matriz[5][1] = 1; snake[0] = &matriz[5][1]; matriz[5][2] = 1; snake[1] = &matriz[5][2]; matriz[5][3] = 1; snake[2] = &matriz[5][3]; headline=5; headcol=3; size=3; gerafruta(); P1OUT |= 0x80; //Pino MR1 (Master reset 1) do CI 74LS90, controla o reset do contador - 1=Reset 0=Count P1OUT &= ~0x80; P1OUT |= 0x20; //Controla o pino BL do CI 4511, acende o display de LED de 7 segmentos } void score(void) { P1OUT |= 0x40; P1OUT &= ~0x40; scorep++; } //______________________________________________________________________________ void acende(void) //Função de clock do shift register { P2OUT &= ~0x01; //DATA = 0(LIGA) P2OUT |= 0x02; //CLOCK ALTO P2OUT &= ~0x02; //CLOCK BAIXO //COLUNAS VERDES -> SINAL BAIXO LIGA / ALTO DESLIGA //Função de controle do SHIFT REGISTER 74LS164 - Pinos(MSP) P2.0(ENTRADA A),P2.1(CLOCK),P2.2(RESET) //Estabelece a situação ligado/desligado de cada LED da linha //O primeiro valor é definido para QA, no sinal de Clock esse valor é transferido para QB e assim por diante até chegar em QH //Serão deslocados 8 valores e então a linha será energizada } //______________________________________________________________________________ void apaga(void) //Função de clock do shift register { P2OUT |= 0x01; //DATA = 1(DESLIGA) P2OUT |= 0x02; //CLOCK ALTO P2OUT &= ~0x02; //CLOCK BAIXO } //______________________________________________________________________________ void desligalinhas(void) { //P1=1111b P1OUT |= 0x0F; //Para desligar todas as colunas basta apontar o DEMUX para a saída 15 não conectada ao circuito //linhas //Função de controle do DEMUX 74154 - Pinos(MSP) P1.0(LSB),1,2,3(MSB) //Seleciona uma saída de 1 a 15 (usadas apenas as 8 primeiras saídas), a saída escolhida recebe sinal baixo, todas as demais sinal alto //Foram utilizados transistores PNP já que a saída do DEMUX é sinal baixo //Usado para acionamento das linhas da Matriz 8x8 } void linha1(void) { P1OUT &= 0xF0; //P1=0000b } void linha2(void) { P1OUT = ((P1OUT&0xF0)|0x01); //P1=0001b //P1OUT é zerado com o comando AND e depois setado com OR } void linha3(void) { P1OUT = ((P1OUT&0xF0)|0x02); //P1=0010b } void linha4(void) { P1OUT = ((P1OUT&0xF0)|0x03); //P1=0011b } void linha5(void) { P1OUT = ((P1OUT&0xF0)|0x04); //P1=0100b } void linha6(void) { P1OUT = ((P1OUT&0xF0)|0x05); //P1=0101b } void linha7(void) { P1OUT = ((P1OUT&0xF0)|0x06); //P1=0110b } void linha8(void) { P1OUT = ((P1OUT&0xF0)|0x07); //P1=0111b } void game_over(void) { desligalinhas(); for (i=0;i<4 br="" i=""> { P1OUT |= 0x20; //P1.5 Controla o pino BL no CI 4511, faz o display de LED de 7 segmentos piscar quando o jogo termina __delay_cycles(500000); P1OUT &= ~0x20; __delay_cycles(500000); } } //______________________________________________________________________________ //FUNÇÃO UP void up(void) { if (headline==0){game_over();reinicia();return;} //se a cobra estiver na borda superior dá gameover - a condição de baixo nao entrou junto no if pois se a headline ja fosse zero ficaria matriz[-1] e daria erro else if (matriz[headline-1][headcol]==1 && snake[0]!=&matriz[headline-1][headcol]){game_over();reinicia();return;} //se o proximo destino colidir com corpo (menos a cauda) da cobra -> gameover else if (matriz[headline-1][headcol]==1 && snake[0]==&matriz[headline-1][headcol] && snake[0]==cresce[0] && cont>0){game_over();reinicia();return;} //caso particular em que a cobra irá crescer, assim ocorre colisao com a cauda -> gameover //else if (snake[size-2]==&matriz[headline-1][headcol]){} //se a cobra tentar retornar sobre si mesmo a jogada será invalida - Se (proximo local = pescoço da cobra) -> jogada invalida else { headline--; //desloca uma linha para cima if (snake[0]==cresce[0] && cont>0) //QUANDO A COBRA DEVE CRESCER - quando a fruta engolida estiver no ultimo segmento da cobra e o contador de frutas engolidas for maior que 0 (caso contrario cresceria passando novamente pelo mesmo local mesmo que sem fruta) //snake[0]=rabo da cobra { size++; cont--; for (i=0;i { cresce[i]=cresce[i+1]; //desloca para a esquerda o vetor de crescimento - para o caso de mais de uma fruta engolida e nao digerida } } else //QUANDO A COBRA NAO PRECISA CRESCER { *snake[0] = 0; //apaga o ultimo ponto da cobra for (i=0;i { snake[i] = snake[i+1]; //movimenta a cobra *snake[i] = 1; } } snake[size-1] = &matriz[headline][headcol]; //snake[size-1] recebe o novo endereço da cabeça da cobra *snake[size-1] = 1; //escreve 1 no endereço if (snake[size-1]==zeros[lugar]) //SE A COBRA ENGOLIR UMA NOVA FRUTA - se o endereço da fruta e da cabeça da cobra forem o mesmo { if(cont==3) {score();score();} else { score(); cresce[cont]=zeros[lugar]; //vetor cresce recebe o endereço da fruta engolida cont++; //novo espaço para guardar mais um endereço de fruta engolida } gerafruta(); } } } //______________________________________________________________________________ //FUNÇÃO DOWN void down(void) { if (headline==7){game_over();reinicia();return;} //se a cobra estiver na borda inferior dá gameover else if (matriz[headline+1][headcol]==1 && snake[0]!=&matriz[headline+1][headcol]){game_over();reinicia();return;} //se o proximo destino colidir com corpo (menos a cauda) da cobra -> gameover else if (matriz[headline+1][headcol]==1 && snake[0]==&matriz[headline+1][headcol] && snake[0]==cresce[0] && cont>0){game_over();reinicia();return;} //caso particular em que a cobra irá crescer, assim ocorre colisao com a cauda -> gameover //else if (snake[size-2]==&matriz[headline+1][headcol]){} //se a cobra tentar retornar sobre si mesmo a jogada será invalida - Se (proximo local = pescoço da cobra) -> jogada invalida else { headline++; //desloca uma linha para baixo if (snake[0]==cresce[0] && cont>0) //QUANDO A COBRA DEVE CRESCER - quando a fruta engolida estiver no ultimo segmento da cobra { size++; cont--; for (i=0;i { cresce[i]=cresce[i+1]; //desloca para a esquerda o vetor de crescimento - para o caso de mais de uma fruta engolida e nao digerida } } else //QUANDO A COBRA NAO PRECISA CRESCER { *snake[0] = 0; //apaga o ultimo ponto da cobra for (i=0;i { snake[i] = snake[i+1]; //movimenta a cobra *snake[i] = 1; } } snake[size-1] = &matriz[headline][headcol]; //snake[size-1] recebe o novo endereço da cabeça da cobra *snake[size-1] = 1; //escreve 1 no endereço if (snake[size-1]==zeros[lugar]) //SE A COBRA ENGOLIR UMA NOVA FRUTA - se o endereço da fruta e da cabeça da cobra forem o mesmo { if(cont==3) {score();score();} else { score(); cresce[cont]=zeros[lugar]; //vetor cresce recebe o endereço da fruta engolida cont++; //novo espaço para guardar mais um endereço de fruta engolida } gerafruta(); } } } //______________________________________________________________________________ //FUNÇÃO LEFT void left(void) { if (headcol==0){game_over();reinicia();return;} //se a cobra estiver na borda inferior -> gameover else if (matriz[headline][headcol-1]==1 && snake[0]!=&matriz[headline][headcol-1]){game_over();reinicia();return;} //se o proximo destino colidir com corpo (menos a cauda) da cobra -> gameover else if (matriz[headline][headcol-1]==1 && snake[0]==&matriz[headline][headcol-1] && snake[0]==cresce[0] && cont>0){game_over();reinicia();return;} //caso particular em que a cobra irá crescer, assim ocorre colisao com a cauda -> gameover else if (snake[size-2]==&matriz[headline][headcol-1]){} //se a cobra tentar retornar sobre si mesmo a jogada será invalida - Se (proximo local = pescoço da cobra) -> jogada invalida else { headcol--; //desloca uma coluna para esquerda if (snake[0]==cresce[0] && cont>0) //QUANDO A COBRA DEVE CRESCER - quando a fruta engolida estiver no ultimo segmento da cobra { size++; cont--; for (i=0;i { cresce[i]=cresce[i+1]; //desloca para a esquerda o vetor de crescimento - para o caso de mais de uma fruta engolida e nao digerida } } else //QUANDO A COBRA NAO PRECISA CRESCER { *snake[0] = 0; //apaga o ultimo ponto da cobra for (i=0;i { snake[i] = snake[i+1]; //movimenta a cobra *snake[i] = 1; } } snake[size-1] = &matriz[headline][headcol]; //snake[size-1] recebe o novo endereço da cabeça da cobra *snake[size-1] = 1; //escreve 1 no endereço if (snake[size-1]==zeros[lugar]) //SE A COBRA ENGOLIR UMA NOVA FRUTA - se o endereço da fruta e da cabeça da cobra forem o mesmo { if(cont==3) {score();score();} else { score(); cresce[cont]=zeros[lugar]; //vetor cresce recebe o endereço da fruta engolida cont++; //novo espaço para guardar mais um endereço de fruta engolida } gerafruta(); } } } //______________________________________________________________________________ //FUNÇÃO RIGHT void right(void) { if (headcol==7){game_over();reinicia();return;} //se a cobra estiver na borda inferior dá gameover else if (matriz[headline][headcol+1]==1 && snake[0]!=&matriz[headline][headcol+1]){game_over();reinicia();return;} //se o proximo destino colidir com corpo (menos a cauda) da cobra -> gameover else if (matriz[headline][headcol+1]==1 && snake[0]==&matriz[headline][headcol+1] && snake[0]==cresce[0] && cont>0){game_over();reinicia();return;} //caso particular em que a cobra irá crescer, assim ocorre colisao com a cauda -> gameover //else if (snake[size-2]==&matriz[headline][headcol+1]){} //se a cobra tentar retornar sobre si mesmo a jogada será invalida - Se (proximo local = pescoço da cobra) -> jogada invalida else { headcol++; //desloca uma coluna para direita if (snake[0]==cresce[0] && cont>0) //QUANDO A COBRA DEVE CRESCER - quando a fruta engolida estiver no ultimo segmento da cobra { size++; cont--; for (i=0;i { cresce[i]=cresce[i+1]; //desloca para a esquerda o vetor de crescimento - para o caso de mais de uma fruta engolida e nao digerida } } else //QUANDO A COBRA NAO PRECISA CRESCER { *snake[0] = 0; //apaga o ultimo ponto da cobra for (i=0;i { snake[i] = snake[i+1]; //movimenta a cobra *snake[i] = 1; } } snake[size-1] = &matriz[headline][headcol]; //snake[size-1] recebe o novo endereço da cabeça da cobra *snake[size-1] = 1; //escreve 1 no endereço if (snake[size-1]==zeros[lugar]) //SE A COBRA ENGOLIR UMA NOVA FRUTA - se o endereço da fruta e da cabeça da cobra forem o mesmo { if(cont==3) {score();score();} else { score(); cresce[cont]=zeros[lugar]; //vetor cresce recebe o endereço da fruta engolida cont++; //novo espaço para guardar mais um endereço de fruta engolida } gerafruta(); } } } //______________________________________________________________________________ void setup_MSP(void) //SETUP DO uC { WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer to prevent time out reset BCSCTL1 = CALBC1_1MHZ; //ajusta o DCO para 1MHz DCOCTL = CALDCO_1MHZ; //ajusta o DCO para 1MHz P1SEL = 0x00; P1SEL2 = 0x00; P1REN = 0x00; //Desabilita o resistor P1DIR = 0xFF; //Definição de pinos OUT (bit=1 -> output) P1OUT = 0xFF; P2SEL = 0x00; P2SEL2 = 0x00; P2REN = 0x00; //Desabilita o resistor P2DIR = 0xFF; //Definição de pinos OUT (bit=1 -> output) P2OUT = 0xFF; //BOTÕES P2DIR &= ~(BIT3+BIT4+BIT5+BIT6); //4 botões -> INPUT P2REN |= (BIT3+BIT4+BIT5+BIT6); //Habilita os resistores P2OUT &= ~(BIT3+BIT4+BIT5+BIT6); //0=Pull-down / 1=Pull-up P2IE = (BIT3+BIT4+BIT5+BIT6); //habilita a interrupção dos pinos P2.3 P2.4 P2.5 P2.6 P2IES = 0; //Interrupção na 0=borda_subida / 1=borda_descida P2IFG = 0; //zera a flag de interrupção // P1IE = 0x0F; //habilita a interrupção dos pinos P1.0 P1.1 P1.2 P1.3 // P1IFG = 0; //zera a flag de interrupção } //______________________________________________________________________________ void setup_TIMER(void) //Setup do timer { TA0CTL = TASSEL_2 + ID_3 + MC_0; //TASSEL (timer_A clock source select) seleciona a fonte do timer, 2=0x10 que indica SMCLK //ID (input divider) 0=0x00(/1) > 1=0x01(/2) > 2=0x10(/4) > 3=0x11(/8) -->QUANTO MAIOR O NUMERO MAIS DEMORA PARA CHEGAR NO VALOR MAXIMO (PISCA MAIS LENTO) //MC (mode control) 0=0x00(stop) > 1=0x01(up) > 2=0x10(continuous) > 3=0x11(up/down) //UP(periodo diferente de 0xFFFF) conta até o valor armazenado no registrador TACCR0 //e seta a flag CCIFG no registrador TACCTL0 quando atinge o valor de comparação no registrador TACCR0 //e seta a flag TAIFG no registrador TACTL quando o timer passa do valor armazenado no registrador TACCR0 para 0 //CONTINUOUS seta flag TAIFG no registrador TACTL quando zera a contagem //UP/DOWN timer conta até TACCR0 e depois decrementa até zero (grafico triangular) //seta flag CCIFG em DOIS MOMENTOS: quando atinge o valor de TACCR0 ou volta para zero TA0CCTL0 = CCIE; //(capture/compare interrupt enable) habilita a interrupção pela flag CCIFG //TA0CCR0 = 0xFFFF; TA0CCTL0 &= ~CCIFG; //limpa flag de interrupção } //______________________________________________________________________________ void imprime(void) //FUNÇAO IMPRIME MATRIZ { for (i=0;i<8 br="" i=""> { desligalinhas(); for(j=0;j<8 br="" j=""> { if (matriz[i][j]!=0) acende(); else apaga(); } if(i==0) linha1(); else if(i==1) linha2(); else if(i==2) linha3(); else if(i==3) linha4(); else if(i==4) linha5(); else if(i==5) linha6(); else if(i==6) linha7(); else linha8(); __delay_cycles(250); } } /* void escrita(void); matriz[i][j] = 1; matriz[i][j] = 1; matriz[i][j] = 1; matriz[i][j] = 1; matriz[i][j] = 1; matriz[i][j] = 1; matriz[i][j] = 1; 0x7f, 0x88, 0x88, 0x88, 0x88, 0x7f, // A 0xff, 0x91, 0x91, 0x91, 0x91, 0x6e, // B 0x7e, 0x81, 0x81, 0x81, 0x81, 0x42, // C 0xff, 0x81, 0x81, 0x81, 0x81, 0x7e, // D 0x81, 0xff, 0x91, 0x91, 0x91, 0x91, // E 0x81, 0xff, 0x91, 0x90, 0x90, 0x80, // F 0x7e, 0x81, 0x81, 0x89, 0x89, 0x4e, // G 0xff, 0x10, 0x10, 0x10, 0x10, 0xff, // H 0x00, 0x81, 0xff, 0xff, 0x81, 0x00, // I 0x06, 0x01, 0x81, 0xfe, 0x80, 0x00, // J 0x81, 0xff, 0x99, 0x24, 0xc3, 0x81, // K 0x81, 0xff, 0x81, 0x01, 0x01, 0x03, // L 0xff, 0x60, 0x18, 0x18, 0x60, 0xff, // M 0xff, 0x60, 0x10, 0x08, 0x06, 0xff, // N 0x7e, 0x81, 0x81, 0x81, 0x81, 0x7e, // O 0x81, 0xff, 0x89, 0x88, 0x88, 0x70, // P 0x7e, 0x81, 0x85, 0x89, 0x87, 0x7e, // Q 0xff, 0x98, 0x98, 0x94, 0x93, 0x61, // R 0x62, 0x91, 0x91, 0x91, 0x91, 0x4e, // S 0xc0, 0x81, 0xff, 0xff, 0x81, 0xc0, // T 0xfe, 0x01, 0x01, 0x01, 0x01, 0xfe, // U 0xfc, 0x02, 0x01, 0x01, 0x02, 0xfc, // V 0xff, 0x02, 0x04, 0x04, 0x02, 0xff, // W 0xc3, 0x24, 0x18, 0x18, 0x24, 0xc3, // X 0xc0, 0x20, 0x1f, 0x1f, 0x20, 0xc0, // Y 0xc3, 0x85, 0x89, 0x91, 0xa1, 0xc3, // Z */ //______________________________________________________________________________ void main (void) //Programa { setup_MSP(); setup_TIMER(); reinicia(); //É POSSIVEL USAR OS COMANDOS ABAIXO NO LUGAR DE time(NULL) //time_t t; //srand((unsigned) time(&t)); //srand( (unsigned)time(NULL) ); /* srand(time(NULL)) alimenta a função rand com uma semente de tempo A função time(NULL) é calculado corresponde ao total de segundos passados desde 1 de janeiro de 1970 até a data atual. Desta forma, a cada execução o valor da "semente" será diferente. */ __bis_SR_register(GIE); //habilita interrupção TA0CTL |= MC_2; //run timer //TA0CTL &= ~MC_2; //stop timer while(1) imprime();//loop infinito } #pragma vector = PORT2_VECTOR __interrupt void interrupt_botao (void) { if (P2IN&0x08 && movimento!=2) movimento=1; //Define a movimentação da cobra executada pela interrupção do timer else if (P2IN&0x10 && movimento!=1) movimento=2; else if (P2IN&0x20 && movimento!=4) movimento=3; else if (P2IN&0x40 && movimento!=3 && movimento!=0) movimento=4; P2IFG = 0; //limpa flag de interrupção } #pragma vector = TIMER0_A0_VECTOR __interrupt void interrupt_timer(void) { if (movimento==1) up(); else if (movimento==2) down(); else if (movimento==3) right(); else if (movimento==4) left(); TA0CCTL0 &= ~CCIFG; //limpa flag de interrupção }8>8> 4>8>8>3>16>36>8>8>4>8>8>4>4>4>
Nenhum comentário:
Postar um comentário