/*
 *  Game: Tiger & sheep.
 *  Copyright (c) 1997 Kengatharan Sivalingam <siva@iki.fi>
 */ 

import java.applet.*;
import java.awt.*;

class Colors {
  final static Color background = new Color(150, 180, 102);  // lightgreen
  final static Color board = Color.black;
  final static Color shadow = Color.black;
  final static Color mode = new Color(65, 105, 225);         // royalblue
  final static Color tiger = Color.red;
  final static Color sheep = Color.yellow;
  final static Color title1 = new Color(247, 247, 247);
  final static Color title2 = new Color(120, 120, 120);
  final static Color result = new Color(255, 239, 213);
}

class Fonts {
  final static Font title1 = new Font("TimesRoman", Font.ITALIC, 20);
  final static Font title2 = new Font("Helvetica", Font.BOLD, 16);
  final static Font turn = new Font("Helvetica", Font.BOLD, 14);
  final static Font mode = new Font("Helvetica", Font.BOLD, 14);
  final static Font result = new Font("Dialog", Font.ITALIC, 48);
}

class GameConsts {
  // All possible legal moves from all 23 positions. 
  // Value -1 is used for illegal positions.
  // Positions:
  //               #0
  //     #1  #2  #3  #4  #5  #6 
  //     #7  #8  #9  #10 #11 #12
  //     #13 #14 #15 #16 #17 #18
  //         #19 #20 #21 #22

  final static int legal_moves[][][] = {              // From pos:
    {{2, 8}, {3, 9}, {4, 10}, {5, 11}},               // 0
    {{2, 3}, {7, 13}, {-1, -1}, {-1, -1}},            // 1
    {{0, -1}, {1, -1}, {3, 4}, {8, 14}},              // 2
    {{0, -1}, {2, 1}, {9, 15}, {4, 5}},               // 3
    {{0, -1}, {3, 2}, {10, 16}, {5, 6}},              // 4
    {{0, -1}, {4, 3}, {11, 17}, {6, -1}},             // 5
    {{5, 4}, {12, 18}, {-1, -1}, {-1, -1}},           // 6
    {{1, -1}, {13, -1}, {8, 9}, {-1, -1}},            // 7
    {{7, -1}, {2, 0}, {14, 19}, {9, 10}},             // 8
    {{8, 7}, {3, 0}, {15, 20}, {10, 11}},             // 9
    {{9, 8}, {4, 0}, {16, 21}, {11, 12}},             // 10
    {{10, 9}, {5, 0}, {17, 22}, {12, -1}},            // 11
    {{11, 10}, {6, -1}, {18, -1}, {-1, -1}},          // 12
    {{7, 1}, {14, 15}, {-1, -1}, {-1, -1}},           // 13
    {{13, -1}, {8, 2}, {19, -1}, {15, 16}},           // 14
    {{14, 13}, {9, 3}, {20, -1}, {16, 17}},           // 15
    {{15, 14}, {10, 4}, {21, -1}, {17, 18}},          // 16
    {{16, 15}, {11, 5}, {22, -1}, {18, -1}},          // 17
    {{17, 16}, {12, 6}, {-1, -1}, {-1, -1}},          // 18
    {{14, 8}, {20, 21}, {-1, -1}, {-1, -1}},          // 19
    {{19, -1}, {15, 9}, {21, 22}, {-1, -1}},          // 20
    {{20, 19}, {16, 10}, {22, -1}, {-1, -1}},         // 21
    {{21, 20}, {17, 11}, {-1, -1}, {-1, -1}}          // 22
  };

  // X- and Y-coordinates for all 23 positions.
  final static int pos_coords[][] = {
    {290, 40}, 
    {20, 190}, {227, 190}, {265, 190}, {315, 190}, {353, 190}, {560, 190},
    {20, 265}, {197, 265}, {253, 265}, {328, 265}, {385, 265}, {560, 265},
    {20, 340}, {166, 340}, {240, 340}, {340, 340}, {415, 340}, {560, 340},
    {40, 640}, {190, 640}, {390, 640}, {540, 640}
  };

  // Text strings
  final static String title1 = "Tiger & Sheep";
  final static String title2_str1 = "Turn:";
  final static String title2_str2 = "Mode:";
  final static String turn_str_tiger = "Tiger";
  final static String turn_str_sheep = "Sheep";
  final static String result_tiger_win = "Tigers win!";
  final static String result_sheep_win = "Sheeps win!";

  // Coordinates for text strings.
  final static int title1_x = 255, title1_y = 27;
  final static int title2_str1_x = 30, title2_str1_y = 130;
  final static int title2_str2_x = 30, title2_str2_y = 155;
  final static int turn_x = 90, turn_y = 130;
  final static int mode_x = 90, mode_y = 155;
  final static int result_x = 212, result_y = 450;

  final static int total_places = 23;
  final static int total_tigers = 3;
  final static int total_sheeps = 14;

  // Legal pieces.
  final static int piece_none = 0;
  final static int piece_tiger = 1;
  final static int piece_sheep = 2;

  // Game status.
  final static int status_play = 0;
  final static int status_tiger_win = 1;
  final static int status_sheep_win = 2;
}

class GameData {
  int board[] = new int[23];
  int placed_tigers = 0;
  int placed_sheeps = 0;
  boolean turn_tiger = true;
  int count_sheeps = 0;
  int dead_sheep_pos = -1;
  int last_cleared_pos = -1;
  int status = 0;
}

public class TigerSheep extends Applet {
  private GameData game;
  private GameConsts consts; 
  private String str_turn_prev, str_mode_prev;
  private int first_click_pos, second_click_pos;

  // Called to initialize the applet.
  public void init() {
    // Set the background color.
    this.setBackground(Colors.background);
    init_game();
  }
  
  // Initialize game.
  public void init_game() {
    game = new GameData();
    consts = new GameConsts();
    game.status = consts.status_play;
    for (int i = 0; i < consts.total_places; i++)
      game.board[i] = consts.piece_none;
    first_click_pos = -1;
    second_click_pos = -1;
    str_turn_prev = new String("");
    str_mode_prev = new String("");
  };
  
  public synchronized void update(Graphics g) {
    paint(g);
  }
  
  public void paint(Graphics g) {
    drawBoard(g);
  }
  
  public void drawBoard(Graphics g) {
    // Clear a dead sheep.
    if (game.dead_sheep_pos != -1) {
      drawSheep(g, game.dead_sheep_pos, true);
      game.dead_sheep_pos = -1;
    }

    // Clear a place from where a piece has been moved.
    if (game.last_cleared_pos != -1) {
      if (game.turn_tiger)
	drawSheep(g, game.last_cleared_pos, true);
      else 
	drawTiger(g, game.last_cleared_pos, true);
      game.last_cleared_pos = -1;
    }

    // Draw an empty board.
    g.setColor(Colors.board);
    g.drawLine(consts.pos_coords[0][0] + 10, consts.pos_coords[0][1] + 10, 
	       consts.pos_coords[19][0] + 10, consts.pos_coords[19][1] + 10);
    g.drawLine(consts.pos_coords[0][0] + 10, consts.pos_coords[0][1] + 10,
	       consts.pos_coords[22][0] + 10, consts.pos_coords[22][1] + 10);
    g.drawLine(consts.pos_coords[19][0] + 10, consts.pos_coords[19][1] + 10, 
	       consts.pos_coords[22][0] + 10, consts.pos_coords[22][1] + 10);
    g.drawLine(consts.pos_coords[0][0] + 10, consts.pos_coords[0][1] + 10, 
	       consts.pos_coords[20][0] + 10, consts.pos_coords[20][1] + 10);
    g.drawLine(consts.pos_coords[0][0] + 10, consts.pos_coords[0][1] + 10, 
	       consts.pos_coords[21][0] + 10, consts.pos_coords[21][1] + 10);
    g.drawLine(consts.pos_coords[7][0] + 10, consts.pos_coords[7][1] + 10, 
	       consts.pos_coords[12][0] + 10, consts.pos_coords[12][1] + 10);
    g.drawRect(consts.pos_coords[1][0] + 10, consts.pos_coords[1][1] + 10, 
	       consts.pos_coords[12][0] - consts.pos_coords[7][0],
	       consts.pos_coords[13][1] - consts.pos_coords[1][1]);

    // Draw pieces.
    for (int i = 0; i < consts.total_places; i++) {
      if (game.board[i] == consts.piece_tiger)
	drawTiger(g, i, false);
      else if (game.board[i] == consts.piece_sheep)
	drawSheep(g, i, false);
    }

    // Draw text strings.
    drawText(g);
  }

  public void drawTiger(Graphics g, int pos, boolean clear) {
     // Draw shadow.
    if (clear)
      g.setColor(Colors.background);
    else 
      g.setColor(Colors.shadow);
    g.fillOval(consts.pos_coords[pos][0] + 2, 
	       consts.pos_coords[pos][1] + 2, 20, 20);

    if (clear)
      g.setColor(Colors.background);
    else 
      g.setColor(Colors.tiger);
    g.fillOval(consts.pos_coords[pos][0], 
	       consts.pos_coords[pos][1], 20, 20);
  }

  public void drawSheep(Graphics g, int pos, boolean clear) {
    // Draw shadow.
    if (clear)
      g.setColor(Colors.background);
    else 
      g.setColor(Colors.shadow);
    g.fillOval(consts.pos_coords[pos][0] + 2, 
	       consts.pos_coords[pos][1] + 2, 20, 20);

    if (clear)
      g.setColor(Colors.background);
    else 
      g.setColor(Colors.sheep);
    g.fillOval(consts.pos_coords[pos][0], 
	       consts.pos_coords[pos][1], 20, 20);
  }

  // Called when the user clicks the mouse.
  public boolean mouseDown(Event e, int x, int y)
  {
    int pos;

    // Ignore mouse events if the game is over.
    if (game.status != consts.status_play)
      return true;

    pos = checkValidity(x, y);

    if (pos < 0) {
      first_click_pos = -1;
      return true;
    }
    
    if (first_click_pos == -1) {
      // Placement.
      if ((game.turn_tiger && game.placed_tigers < consts.total_tigers) ||
	  (!game.turn_tiger && game.placed_sheeps < consts.total_sheeps)) {
	first_click_pos = pos;
	movePiece(true);
	first_click_pos = -1;
      } else {
	// First position should be occupied with appropriate piece.
	if ((game.turn_tiger && game.board[pos] == consts.piece_tiger) ||
	    (!game.turn_tiger && game.board[pos] == consts.piece_sheep))
	  first_click_pos = pos;
      }	  
    } else {
      // The new position should be empty.
      if (game.board[pos] == consts.piece_none) {
	second_click_pos = pos;
	movePiece(false);
      }
      first_click_pos = -1;
      second_click_pos = -1;
    }
    return true;
  }

  // Verify validity of a coordinate. Return corresponding position 
  // number for a valid coordinate.
  public int checkValidity(int x, int y) {
    for (int i = 0; i < consts.total_places; i++) {
      if ((x >= consts.pos_coords[i][0]) &&
	  (x <= consts.pos_coords[i][0] + 20) &&
	  (y >= consts.pos_coords[i][1]) &&
	  (y <= consts.pos_coords[i][1] + 20))
	return i;
    }
    return -1;
  }
  
  // Place or move a piece.
  public void movePiece(boolean placement) {
    boolean move_made = false;

    // The position is occupied by some other piece.
    if (placement && game.board[first_click_pos] != consts.piece_none)
	return;

    /* Tigers's turn. */
    if (game.turn_tiger) {
      if (placement) {
	game.board[first_click_pos] = consts.piece_tiger;
	game.placed_tigers++;
      } else {
	for (int i = 0; i < 4; i++) {
	  // Move to an empty position.
	  if (second_click_pos == consts.legal_moves[first_click_pos][i][0]) {
	    game.board[first_click_pos] = consts.piece_none;
	    game.board[consts.legal_moves[first_click_pos][i][0]] =
	      consts.piece_tiger;
	    
	    move_made = true;
	    game.last_cleared_pos = first_click_pos;
	    break;
	  } else if (second_click_pos == 
		     consts.legal_moves[first_click_pos][i][1]) {
	    // Hunt a sheep.
	    if (game.board[consts.legal_moves[first_click_pos][i][0]] ==
		consts.piece_sheep)
	      game.board[first_click_pos] = consts.piece_none;
	    // Free hunted sheep's position.
	    game.board[consts.legal_moves[first_click_pos][i][0]] =
	      consts.piece_none;
	    game.board[consts.legal_moves[first_click_pos][i][1]] =
	      consts.piece_tiger;

	    move_made = true;
	    game.last_cleared_pos = first_click_pos;
	    game.dead_sheep_pos = consts.legal_moves[first_click_pos][i][0];
	    game.count_sheeps--;
	    break;
	  }
	}
	if (!move_made)
	  return;
      }
      game.turn_tiger = false;
    } else {
      // Sheeps' turn.
      if (placement) {
	game.board[first_click_pos] = consts.piece_sheep;
	game.placed_sheeps++;
	game.count_sheeps++;
      } else {
	for (int i = 0; i < 4; i++) {
	    // Move to an empty position.
	  if (second_click_pos == consts.legal_moves[first_click_pos][i][0]) {
	    game.board[first_click_pos] = consts.piece_none;
	    game.board[consts.legal_moves[first_click_pos][i][0]] =
	      consts.piece_sheep;
	    
	    move_made = true;
	    game.last_cleared_pos = first_click_pos;
	    break;
	  }
	}
	if (!move_made)
	  return;
      }
      game.turn_tiger = true;
    }

    // Start evaluting the game only after placing all the tigers. 
    if (game.placed_tigers == consts.total_tigers )
      evalGame();

    repaint();
  }

  // Evaluate current game. This version verifies only game 
  // winning positions.
  public void evalGame() {
    int trapped_tigers = 0;
    int closed_paths = 0;
    
    // No sheep left.
    if (game.count_sheeps == 0) {
      game.status = consts.status_tiger_win;
      return;
    }

    // Look for trapped tigers.
    for (int i = 0; i < consts.total_places; i++) {
      closed_paths = 0;

      if (game.board[i] == consts.piece_tiger) {
	for (int j = 0; j < 4; j++) 
	  if (consts.legal_moves[i][j][0] == -1 || 
	      game.board[consts.legal_moves[i][j][0]] == consts.piece_tiger || 
	      (game.board[consts.legal_moves[i][j][0]] == consts.piece_sheep &&
	       (consts.legal_moves[i][j][1] == -1 ||
		game.board[consts.legal_moves[i][j][1]] == consts.piece_sheep ||
		game.board[consts.legal_moves[i][j][1]] == consts.piece_tiger)))
	    closed_paths++;
      } else 
	continue;
      
      if (closed_paths == 4)
	trapped_tigers++;
      else 
	break;
    }
    
    // All three tigers are trapped.
    if (trapped_tigers == 3) {
      game.status = consts.status_sheep_win;
      return;
    }
  }

  public void drawText(Graphics g) {
    String str_turn, str_mode;
    int left;

    // Program title. 
    g.setFont(Fonts.title1);
    // Draw shadow text first.
    g.setColor(Colors.shadow);
    g.drawString(consts.title1, consts.title1_x + 2, consts.title1_y + 2);
    g.setColor(Colors.title1);
    g.drawString(consts.title1, consts.title1_x, consts.title1_y);

    if (game.status != consts.status_play)
      g.setColor(Colors.background);
    else 
      g.setColor(Colors.title2);

    // Title text for turn and mode.
    g.setFont(Fonts.title2);
    g.drawString(consts.title2_str1, consts.title2_str1_x, 
		 consts.title2_str1_y);
    g.drawString(consts.title2_str2, consts.title2_str2_x, 
		 consts.title2_str2_y);

    // Draw turn and mode text.
    // Erase first existing strings.
    g.setColor(Colors.background);
    g.setFont(Fonts.turn);
    g.drawString(str_turn_prev, consts.turn_x, consts.turn_y);
    g.setFont(Fonts.mode);
    g.drawString(str_mode_prev, consts.mode_x, consts.mode_y);

    if (game.status == consts.status_play) {
      if (game.turn_tiger) {
	g.setColor(Colors.tiger);
	str_turn = consts.turn_str_tiger;
	str_turn_prev = consts.turn_str_tiger;
	left = consts.total_tigers - game.placed_tigers;
      } else {
	g.setColor(Colors.sheep);
	str_turn = consts.turn_str_sheep;
	str_turn_prev = consts.turn_str_sheep;
	left = consts.total_sheeps - game.placed_sheeps;
      }

      g.setFont(Fonts.turn);
      g.drawString(str_turn, consts.turn_x, consts.turn_y);

      g.setFont(Fonts.mode);
      g.setColor(Colors.mode);
      if (left > 0)
	str_mode = new String("Place (" + left + " left)");
      else 
	str_mode = new String("Move");

      str_mode_prev = str_mode;
      g.drawString(str_mode, consts.mode_x, consts.mode_y);
    } else {
      // Text for game result.
      g.setFont(Fonts.result);

      if (game.status == consts.status_tiger_win) {
	g.setColor(Colors.shadow);
	g.drawString(consts.result_tiger_win, consts.result_x + 2, 
		     consts.result_y + 2);
	g.setColor(Colors.result);
	g.drawString(consts.result_tiger_win, consts.result_x, consts.result_y);
      } else {
	g.setColor(Colors.shadow);
	g.drawString(consts.result_sheep_win, consts.result_x + 2, 
		     consts.result_y + 2);
	g.setColor(Colors.result);
	g.drawString(consts.result_sheep_win, consts.result_x, consts.result_y);
      }
    }
  }
}
