/*This applet plays memory
 made by Sieuwert van Ottterloo, 1998
*/


import java.awt.*;//basic
import java.applet.*;//because this is an applet
import java.net.*;//because we load gif files

public class memory extends Applet
//the main of the applet
{
    table t;//the panel with the cards on it
    Button newgame,surrender;//pressable buttons
    Label l1;//to show the scores
    
    public void init()//preparations
    {
        resize(9*table.SIZE+20,8*table.SIZE+70);
        setBackground(Color.white);
        Panel buttons=new Panel();//to keep the button together
        buttons.setLayout(new GridLayout(1,3,2,2));//3 positions 
        buttons.add(newgame=new Button("New game"));
        buttons.add(surrender=new Button("Show me all"));
        l1=new Label();
        l1.setFont(new Font("Arial",Font.PLAIN,20));
        l1.setBackground(Color.green);
        buttons.add(l1);
        add("Center",t=new table(this,l1));
        add("South",buttons);
    }
    public boolean action(Event ev, Object obj)
    /*a button is touched*/
    {
        if(ev.target==newgame)
            t.randomcards();//shuffle the cards
        else if(ev.target==surrender)
            t.showall();//show all cards
     return true;
    }

}

class table extends Canvas
{
 int[] visbuf;//what can be seen on table
 int[] card;//where are the cards
 /*card stores the places of the cards. A card entry is either NONE, 
 if the card that was originally there is removed, 
 or a picturenumber (0 to and including 35).
 Visbuf can also have the value BACK, if the back of the
 card is visible. */
 Applet applet;//needed to get the documentbase
 final static int TOTAL=72;//total # of cards in play
 final static int BACK=36;//a facedown card
 final static int NONE=-2;//no card at all
 final static int SIZE=52;//cardsize (in pixels)
 final static int PICOFFSET=1;//#pixels the pictures are translated.
 final static int PCMEMSIZE=16;
 int pos1,pos2;/*position of the open cards.*/
 final static int USER1=21,USER2=22,USER3=23,PC1=24,PC2=25,PC3=26,WAIT=27;
 int state;
 /*The state determines what happens after a mouseclick.*/
 int pcmemstart;
 int pcchoice1,pcchoice2;
 int[] pcmem;
 Color gray=new Color(204,204,204);
 
 int pointspc,pointsuser;
 Image[] picture;//the pictures used
 Label l1;//for messages
 
 table(Applet a,Label a_label)
 {
  setBackground(Color.gray);
  l1=a_label;//the label for messages
  applet=a;//the applet we are running in. 
  visbuf=new int[TOTAL];
  card=new int[TOTAL];
  pcmem=new int[PCMEMSIZE];
  resize(9*SIZE,8*SIZE);//the size needed to display it all
  picture= new Image[37];//36 cards, plus BACK
  URL base=applet.getDocumentBase();
  for(int i=0;i<37;i++)
  {try
    {picture[i]=applet.getImage(new URL(base,i+".gif"));}
   catch(MalformedURLException e)
    {System.out.println("loading failed");}
  } 
  randomcards();//shuffle all
  showall();
 }
 
 int pcmakechoice1()
 {
  //seek a pair of cards if possible...
  for(int i=0;i<PCMEMSIZE;i++)
   if(pcmem[i]!=-1&&visbuf[pcmem[i]]==BACK)
    for(int j=i+1;j<PCMEMSIZE;j++)
     if(pcmem[j]!=-1
        &&pcmem[i]!=pcmem[j]
        &&card[pcmem[i]]==card[pcmem[j]])
            {return pcmem[i];
            }
   //or pick a card you haven't seen:
   return pcunseencard();
 }
 
 int pcunseencard()
 { int result;
   boolean good;
   do
   {
    good=true;
    result=(int)(Math.random()*TOTAL);
    if(visbuf[result]!=BACK)
        good=false;
    for(int i=0;i<PCMEMSIZE;i++)
        if(pcmem[i]==result)
            good=false;
   }
   while(!good);
   return result;
 }
 
 int pcmakechoice2()
 {
   for(int i=0;i<PCMEMSIZE;i++)
        if(pcmem[i]!=-1&&
           card[pos1]==card[pcmem[i]]&&
           pos1!=pcmem[i])
            {return pcmem[i];            
            }
   return pcunseencard();    
 }
 
 
 void clearpcmem()
 {
    for(int i=0;i<PCMEMSIZE;i++)
        pcmem[i]=-1;
    pcmemstart=0;
 }
 
 void pcremember(int card)
 {
    pcmem[pcmemstart]=card;
    pcmemstart=(pcmemstart+1)%PCMEMSIZE;
 }
 
 void showall()//show all cards
 {
    for(int i=0;i<TOTAL;i++)
        visbuf[i]=card[i];//set cards in visbuf
    repaint();//update the screen
    state=WAIT;//in the waitstate mouseclicks are ignored.
 }

 void addpoints(int a,int b)
 {
  pointsuser+=a;
  pointspc-=b;
  showscores();
 }
 
 void showscores()
 {
    l1.setText(" "+pointsuser+"/"+pointspc);
 }

 void randomcards()
 //shuffle the cards, lay them facedown on table
 {
    clearpcmem();
    pointspc=pointsuser=0;
    showscores();
    for(int i=0;i<TOTAL;i++)
        {visbuf[i]=BACK;
         card[i]=NONE;
        }
    state=USER1;
    repaint();//we have changed the visbuf
    for(int cards=0;cards<TOTAL;cards++)
    {//for any card we lay down
        int r=(int)(Math.random()*(TOTAL-cards));
        //r is one of the empty spots.
        int i=0;
        while(true)
        //we are searching the r'th empty spot.
        {
            if(card[i]==NONE)
                {r--;}//empty spot found
            if(r<0)//enough empty spots found
                break;//leave the loop
            else
                i++;//search ahead
        }
        card[i]=cards/2;//lay the card at the empty spot
        //every number between 0 and 35 is laid down twice
    }
 }

 public boolean mouseDown(Event ev,int x,int y)
 /*There are 6 possibilities:
 -> state==USER1: no card open yet. Store the clicked
 card in pos1, and turn it around.
 -> state==USER2.One card allready turned around.
 Turn the clicked card, and store it in pos2
 -> state==USER3: two cards open. if the same, remove, else
 lay them face down.
 PC1,PC2,PC3 are the same for the PC to move.
 */
 {
  if(state==WAIT)
    return true;
  if(pointspc+pointsuser==36)
    return true;  
  if(state==USER3||state==PC3)//already two cards open
  {
   if(card[pos2]==card[pos1])//same cards
    {visbuf[pos2]=card[pos2]=NONE;
     visbuf[pos1]=card[pos1]=NONE;//remove the cards
     if(state==USER3)
     {
        addpoints(1,0);
        state=USER1;
     }
     else
     {
        addpoints(0,1);
        state=PC1;        
     }
    }
   else
   {
    visbuf[pos2]=visbuf[pos1]=BACK;//flip them back
    state=(state==USER3)?PC1:USER1;
   }
   repaint();       
   return true;  
  }
  
  //one or no card open:
  if(state==USER1||state==USER2)
  {
    x=x/SIZE;
    y=y/SIZE;//calcuate coordinates in cards.
    int pos=x+9*y;//conversion to 1D coordinates
    if(x<0||x>=9||y<0||y>=8||visbuf[pos]!=BACK)
        return true; 
    /*The mouseclick must be inside the table, and point
    at a facedown card.*/
    visbuf[pos]=card[pos];//show the card
    pcremember(pos);
    repaint();
    if(state==USER1)
        pos1=pos; //just store it  
    else 
        pos2=pos;//also store it  
    state++;
    return true;
  }
  else if(state==PC1)
  {
    pos1=pcmakechoice1();
    visbuf[pos1]=card[pos1];//show the card
    pcremember(pos1);
    repaint();
    state++;
  }
  else 
  {    
    pos2=pcmakechoice2();
    visbuf[pos2]=card[pos2];//show the card
    pcremember(pos2);
    repaint();
    state++;    
  }  
  return true;    
 }
 
 public void paint(Graphics g)
 /*paint all cards. They are drawen like they are in 
 visbuf: Most of them facedown. */
 {
  for(int i=0;i<TOTAL;i++)
  {int x=i%9;
   int y=i/9;//conversion to 2D coordinates
   paintfield(g,x,y,visbuf[i]);
  }
 }
 
 void paintfield(Graphics g,int x,int y,int value)
 /*displays a field. value can be 0 to 35, indicating a
 picture, or BACK(=36), or NONE.*/
 {
    int sx=x*SIZE;
    int sy=y*SIZE;
    g.setColor(gray);
    g.fillRect(sx,sy,SIZE,SIZE);    
    if(value!=NONE)
    drawimage(g,sx+PICOFFSET,sy+PICOFFSET,value);
 }    
 
 void drawimage(Graphics g,int sx,int sy,int imno)
 /*draws one of the pictures at the specified position.*/
 {
  if(g.drawImage(picture[imno],sx,sy,null)!=true)
  {
    g.setColor(Color.white);
    g.fillRect(sx+PICOFFSET,sy+PICOFFSET,
                    SIZE-PICOFFSET,SIZE-PICOFFSET);         
  }  
    /*if the picture is not available yet, we draw
    a gray square instead.*/
 }
  
 
 public void update(Graphics g)
 //if not redefined, update clears the screen and then paints.
 //this causes flickering.
 {   paint(g);}
 
}