Recrie o clássico jogo de cobra


11

O desafio é criar o clássico jogo de cobra usando o mínimo de bytes possível.

Aqui estão os requisitos:

  • O jogo deve ser implementado em um layout bidimensional típico. A cobra deve ser capaz de crescer significativamente dentro dos limites do mapa (isso realmente significa, não faça seu mapa muito pequeno, use sua discrição aqui).
  • Um usuário pode mover a cobra usando as chaves de sua escolha; no entanto, a cobra não pode se dobrar (por exemplo, se estiver indo para o oeste, não poderá ir para o leste sem primeiro ir para o norte ou sul). Uma cobra deve poder viajar em todas as 4 direções: cima, baixo, esquerda, direita (norte, sul, oeste, leste).
  • A cobra começa com o comprimento 1, cada vez que come um objeto de "comida" cresce +1 em comprimento
  • Os objetos alimentares são colocados aleatoriamente em locais que não sejam os ocupados pela cobra
  • Se a Cobra bater em si mesma ou em uma parede, o jogo termina
  • Quando o jogo termina, o literal "Score: [score]" é exibido onde [score] é o número de itens alimentares consumidos durante o jogo. Por exemplo, se a cobra comer 4 "comidas" (e, portanto, tiver 5) quando o jogo terminar, "Pontuação: 4" será impressa.
  • Não há algoritmos de compactação, a menos que estejam explicitamente definidos no seu código.

Aqui está a minha solução, 908 bytes, Python 2.7

import random as r
import curses as c
def g(s,w,l):
 while 1:
  p=[r.randrange(0,w),r.randrange(0,l)]
  for l in s:
   if l==p:continue
  return p
s=[]
d=[0,1]
p=k=n=0
e=100
v={65:[-1,0],66:[1,0],68:[0,-1],67:[0,1]}
z=c.initscr()
w,l=z.getmaxyx()[0],z.getmaxyx()[1]
c.noecho()
z.clear()
x=g(s,w,l)
s.append([w/2,l/2])
z.nodelay(1)
q=lambda h,i:range(h,len(i))
while k!=101:
 k=z.getch()
 if k in v and not (d[0]==(v[k][0]*-1) and d[1]==(v[k][1]*-1)):d=v[k]
 f=[0,0]
 for i in q(0,s):
  if i == 0:
   f=[s[i][0],s[i][1]]
   s[i][0]+=d[0]
   s[i][1]+=d[1]
  else:s[i],f=f,s[i]
 if s[0]==x:
  n+=1
  s.append(f)
  x=g(s,w,l)
 z.clear()
 if s[0][0]>=w or s[0][1]>=l or s[0][0]<0 or s[0][1]<0:break
 for i in q(1,s):
  if s[0] == s[i]: k = 101
 for i in q(0,s):z.addch(s[i][0],s[i][1],"X")
 z.addch(x[0],x[1],"O")
 z.move(0,0)
 z.refresh()
 if d[1]!=0:c.napms(e/2)
 else:c.napms(e)
c.endwin()
print 'Score: %s'%n


1
@ copie algumas pessoas não gostam de ficar restritas aos terminais.
Griffin

a regra 'cobra não pode voltar atrás' se aplica se a cobra tiver comprimento = 1?
Paul Prestidge 10/09/12

@chron, sim, sim. Em todos os momentos, as cobras só podem (de verdade) girar em duas direções, esquerda e direita.
Mjgpy3

Respostas:


2

Ruby 1.9 + SDL (341 324 316)

Aqui está uma primeira tentativa de uma versão Ruby usando a biblioteca SDL. Posso salvar 6 caracteres se for permitido carregar a biblioteca SDL usando -rsdlna linha de comando, em vez da instrução require.

require'sdl'
f=o=d=3
s=SDL::Screen.open l=32,l,0,0
r=*0..l*l
loop{f==o ?f=(r-$*).sample: $*.shift
/yU/=~"#{e=SDL::Event.poll}"&&(v=e.sym%4)&&d+v!=3&&d=v
$><<"Score #{$*.size}"&&exit if$*.index(n=o+[-1,-l,l,1][d])||n<0||n>=l*l||d%3<1&&n/l!=o/l
$*<<o=n
r.map{|i|s[i%l,i/l]=[[f,*$*].index(i)?0:255]*3}
s.flip
sleep 0.1}

Os segmentos de cobra e pedaços de comida são representados usando pixels pretos, o tamanho da grade é atualmente 32 * 32. Você pode controlar com as teclas de seta (ou qualquer outra tecla, o código de chave mod 4 indexa a matriz de direção [ESQUERDA, CIMA, BAIXO, DIREITA]). Eu acho que definitivamente há espaço para melhorias aqui, especialmente na declaração SE da verificação da morte.

Eu melhorei bastante isso em relação à versão anterior, e espero que ela se aproxime mais do espírito da questão agora. Há uma coisa que preciso corrigir para cumprir as especificações, que é a de que alimentos podem aparecer no interior da cauda. Fixo!

Imprime a pontuação em stdout após o término do jogo.


2

Java, 2343 2239

Não é exatamente conciso, mas acredito que segue todos os requisitos.

Classe cobra

import javax.swing.*;
public class S extends JFrame{
S(){add(new B());setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);setSize(320,340);setVisible(true);}
public static void main(String[]a){new S();}}

Classe de diretoria

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class B extends JPanel implements ActionListener{
int W=300;int H=300;int DS=10;int AD=900;int RP=29;int D=140;int x[]=new int[AD];int y[]=new int[AD];int d;int ax;int ay;boolean l=false;boolean r=true;boolean u=false;boolean dn=false;boolean ig=true;Timer t;Image b;Image a;Image h;
B(){addKeyListener(new T());setBackground(Color.black);ImageIcon id=new ImageIcon(this.getClass().getResource("d.png"));b=id.getImage();ImageIcon ia=new ImageIcon(this.getClass().getResource("a.png"));a=ia.getImage();ImageIcon ih=new ImageIcon(this.getClass().getResource("h.png"));h=ih.getImage();setFocusable(true);i();}
void i(){d=3;for(int z=0;z<d;z++){x[z]=50-z*10;y[z]=50;}l();t=new Timer(D,this);t.start();}
public void p(Graphics g){super.paint(g);if(i){g.drawImage(a,ax,ay,this);for(int z=0;z<d;z++){if(z==0)g.drawImage(h,x[z],y[z],this);else g.drawImage(b,x[z],y[z],this);}Toolkit.getDefaultToolkit().sync();g.dispose();}else{g(g);}}
void g(Graphics g){String ms="Score:";Font sm=new Font("Courier",Font.PLAIN,12);FontMetrics me=this.getFontMetrics(sm);g.setColor(Color.white);g.setFont(sm);g.drawString(ms+d,(W-me.stringWidth(ms)),H);}
void c(){if((x[0]==ax)&&(y[0]==ay)){d++;l();}}
void m(){for(int z=d;z>0;z--){x[z]=x[(z-1)]; y[z]=y[(z-1)];}if(l){x[0]-=DS;}if (r){x[0]+=DS;}if(u){y[0]-=DS;}if(dn){y[0]+=DS;}}
void cc(){for(int z=d;z>0;z--){if((z>4)&&(x[0]==x[z])&&(y[0]==y[z])){ig=false;}}if(y[0]>H){ig=false;}if(y[0]<0){ig=false;}if(x[0]> W){ig=false;}if(x[0]<0){ig=false;}}
void l(){int r=(int)(Math.random()*RP);ax=((r*DS));r=(int)(Math.random()*RP);ay=((r*DS));}
public void actionPerformed(ActionEvent e){if(ig){c();cc();m();}repaint();}
class T extends KeyAdapter{public void keyPressed(KeyEvent e){int k=e.getKeyCode();if((k==KeyEvent.VK_LEFT)&&(!r)){l=true;u=false;dn=false;}if((k==KeyEvent.VK_RIGHT)&&(!l)){r=true;u=false;dn=false;}if((k==KeyEvent.VK_UP)&&(!dn)){u=true;r=false;l=false;}if((k==KeyEvent.VK_DOWN)&&(!u)){dn=true;r=false;l=false;}}}}

Captura de tela

jogo de cobra em java


Comentário

Há algum tempo, visitei um site chamado zetcode, que fornecia alguns tutoriais para a criação de jogos 2D clássicos em Java. O código fornecido é fortemente influenciado pelo tutorial que foi fornecido para o jogo Snake ... Acho que, nesse momento, comecei a codificar jogos clássicos e segui o tutorial para um 'T'.

Farei uma edição mais tarde e adicionarei um link a um executável para que as pessoas possam jogar.


EDITAR% S

  • 9/9/12: Não consigo carregar corretamente as imagens da pasta de recursos. Continuarei a resolver esse problema na tentativa de provar que meu código funciona e atende a todos os critérios da pergunta.
  • 11/09/12: Continuarei trabalhando para que as imagens sejam carregadas do arquivo de recurso. Adicionei uma imagem fornecida no tutorial do ZetCode.

Ótimo, estou ansioso para tentar!
Mjgpy3 10/09/12

Existe um link para o arquivo executável em rota :)
Drenai

@BrianBishop Desculpe cara, eu nunca entendi o que estava fazendo incorretamente com meus arquivos de imagem no arquivo de recursos. Tudo é compilado, mas as imagens nunca aparecem.
Rob

2

Bash: 537 533 507 caracteres

C=$COLUMNS;L=$LINES;D=-1;c=9;r=9;z=(9\ 9);l=;h=1;v=;s=1;d=1
t(){ echo -en "\e[$2;$1H$3";}
b(){ ((f=RANDOM%C+1));((g=RANDOM%L+1));for i in "${z[@]}";do [[ $f\ $g = $i ]]&&b;done;t $f $g F;}
echo $'\e[2J';b
while :;do
read -sn1 -t.1 k
case $k in
w|s)((h))&&h=&&v=${D:$k};;
a|d)((v))&&v=&&h=${D:$k};;
esac
((c+=h));((r+=v))
((c==f&&r==g&&++l))&&b
((c<1||r<1||c>C||r>L))&&break
for i in "${z[@]}";do [[ $c\ $r = $i ]]&&break 2;done
t ${z[-1]} \ ;t $c $r X
z=($c\ $r "${z[@]::l}")
done
echo $'\e[2J\e[H'Score: $l

Como ele usa os $COLUMNSe $LINESvariáveis do shell, ele deve ser executado de origem: . snake.sh. A cobra pode ser controlada com os w/ a/ s/ dchaves.

Eu sei, ele pode ser facilmente reduzido para 493 caracteres usando clearpara limpar a tela, mas eu prefiro mantê-lo puro bash, sem usar nenhuma ferramenta externa.


Solução muito legal!
Mjgpy3 11/09/12

1

Python 2.7: 869 816 818 817 816 caracteres

Eu cortei isso juntos nas últimas horas. Ele deve atender aos requisitos e é alguns caracteres menor que a solução de mjgpy3 (tentei muito, mas não consegui obtê-lo muito mais curto. Agora estou cansado). Surpreendentemente, o uso de uma biblioteca de desenvolvimento de jogos como o pygame não reduziu muito a python-snake. Sugestões e dicas sobre como reduzi-lo são muito apreciadas. Espero que não seja muito enigmático.

Este é o resultado:

import pygame as p
from random import randint as r
p.init();l=20
c=p.time.Clock()
dp=p.display;w=p.display.set_mode((500,)*2)
C=p.Color;b=C(0,0,0);g=C(0,99,0)
D=(0,1);U=(0,-1);L=(-1,0);R=(1,0)
S=[R];d=R;n=[]
O=lambda t:{U:D,R:L,D:U,L:R}[t]
def Q(e):print "Score: %i"%(len(S)-1);p.quit()
def K(e):global d;_={276:L,273:U,274:D,275:R}.get(e.key,(0,0));d=not _==O(d) and _ or d
def N(S):[p.draw.rect(w,g,[x[0]*l,x[1]*l,l,l]) for x in S+n] 
def M():n=(r(0,24),r(0,24));return n not in S and n or M()
A=lambda s,o:tuple(x+y for x,y in zip(s,o))
n=[M()] 
while True:
 w.fill(b);[{12:Q,2:K}.get(e.type,lambda e:e)(e) for e in p.event.get()]
 if not (0<=S[-1][0]<25 and 0<=S[-1][1]<25) or A(S[-1],d) in S: Q(e) 
 if A(S[-1],d) in n: S.append(A(S[-1],d));n=[M()]
 else: S.append(A(S[-1],d));S.pop(0)
 N(S);dp.update();c.tick(6)

EDIT: Eu poderia reduzi-lo para 816 bytes, yay! :) Corrigido o placar

EDIT2: colou a versão errada acidentalmente

Aqui está uma versão comentada:

import pygame as p
from random import randint as r

# initialize pygame
p.init()

# the game consists of 25*25 blocks,with each block 20*20 pixels
l=20

# initialize the main loop clock
c=p.time.Clock()

# open the window
dp=p.display;w=p.display.set_mode((500,)*2)

# define black and green colors
C=p.Color;b=C(0,0,0);g=C(0,99,0)

# Directions of the snake: down, up, left, right
D=(0,1);U=(0,-1);L=(-1,0);R=(1,0)

# S is the snake, d is the current direction and n is the array of foods
S=[R];d=R;n=[]

# get the opposite direction of a direction to forbid double backing
O=lambda t:{U:D,R:L,D:U,L:R}[t]

# print the score and quit
def Q(e):print "Score: %i"%(len(S)-1);p.quit()

# update the direction (this is a key press handler)
def K(e):global d;_={276:L,273:U,274:D,275:R}.get(e.key,(0,0));d=not _==O(d) and _ or d

# draw the snake and food boxes
def N(S):[p.draw.rect(w,g,[x[0]*l,x[1]*l,l,l]) for x in S+n]

# place new food on the map not colliding with the snake
def M():n=(r(0,24),r(0,24));return n not in S and n or M()

# A((1,1), (-2, 1)) -> (-1,2)
A=lambda s,o:tuple(x+y for x,y in zip(s,o))

# initialize food array
n=[M()]

while True:
 # fill the screen black
 w.fill(b)
 # get quit or key press events and execute the event handlers
 [{12:Q,2:K}.get(e.type,lambda e:e)(e) for e in p.event.get()]

 # check if snake hits map boundaries or itself
 if not (0<=S[-1][0]<25 and 0<=S[-1][1]<25) or A(S[-1],d) in S: Q(e)

 # check if snake is eating food at the moment and append one to the snake's length
 if A(S[-1],d) in n: S.append(A(S[-1],d));n=[M()]

 # move the snake in the current direction
 else: S.append(A(S[-1],d));S.pop(0)

 # draw the map and limit the main loop to 6 frames per second
 N(S);dp.update();c.tick(6)

Eu recebi a mensagem de erro "Falha na segmentação (núcleo despejado)". E parece que a pontuação está 1 em 1 (não é realmente um grande negócio. Embora a resposta seja muito legal.
#

2
Obrigado :) Também recebo a mensagem fauklt da segmentação. Ainda não descobri. Corrigido o placar e reduzido o tamanho :) isso é divertido.
stefreak 24/09/12

1
você poderia fazer o verde mais escuro, em vez de 255, o uso de 99, então isso vai ser um byte retirado
KrystosTheOverlord

@KrystosTheOverlord hahah sim bom ponto: D
stefreak 22/03/19
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.