#
# queens.jako
#
# A program to find solutions to the 8-queens problem.
#
# Copyright (C) 2001-2005, The Perl Foundation.
# This program is free software. It is subject to the same
# license as the Parrot interpreter.
#
# $Id: /mirror/trunk/languages/jako/examples/queens.jako 12840 2006-05-30T15:08:05.048089Z coke  $
#

use sys;
use string;

const int NUM_FILES = 8;
const int NUM_RANKS = 8;


#
# remove_queen()
#

sub str remove_queen (str board, int rank, int file)
{

  var int i;
  i  = rank * NUM_FILES;
  i += file;

  var int x;
  x = i;

  var int y;
  y = i + 1;

  var int z;
  z = string::length(board);
  z = z - y;

  var str prefix;
  prefix = string::substr(board, 0, x);
  var str suffix;
  suffix = string::substr(board, y, z);

  var str temp;
  temp = prefix;
  temp = string::concat(temp, " ");
  temp = string::concat(temp, suffix);

  return temp;
}


#
# clear_file()
#
# Clears the queen from the current file. Makes no assumption about current
# nybble contents.
#

sub str clear_file (str board, int file)
{
  var int rank = 0;

  while (rank < NUM_RANKS) {
    board = remove_queen(board, rank, file);
    rank++;
  }

  return board;
}


#
# place_queen()
#
# Places a queen at the given rank and file, removing any other queen from the
# file.
#

sub str place_queen (str board, int rank, int file)
{
  board = clear_file(board, file);

  var int i;
  i  = rank * NUM_FILES;
  i += file;

  var int x;
  x = i;

  var int y;
  y = i + 1;

  var int z;
  z = string::length(board);
  z = z - y;

  var str prefix;
  prefix = string::substr(board, 0, x);
  var str suffix;
  suffix = string::substr(board, y, z);

  var str temp;
  temp = prefix;
  temp = string::concat(temp, "Q");
  temp = string::concat(temp, suffix);

  return temp;
}


#
# queen_rank()
#
# Fetches the queen's rank.
#
# Input:  file
# Ouptut: rank = queen's rank in that file
#

sub int queen_rank (str board, int file)
{
  var int rank = 0;

  while (rank < NUM_RANKS) {
    var int temp;
    temp = queen_at(board, rank, file);
    return rank if (temp == 1);
    rank++;
  }
 
  return -1;
}


#
# at()
#
# Determines whether or not there is a queen at a given location.
#
# Input:  rank, file
# Output: 1 (queen) or 0 (empty)
#

sub str at (str board, int rank, int file) {
  var str temp;

#  print("Fetching contents of square at $rank, $file...\n");

  return 0 if (rank < 0);
  return 0 if (rank >= NUM_RANKS);
  return 0 if (file < 0);
  return 0 if (file >= NUM_FILES);

  var int i;
  i  = rank * NUM_FILES;
  i += file;
 
  var int l;
  l = string::length(board);

#  print("Board is $l characters long. Fetching character at index $i.\n");

  temp = string::substr(board, i, 1);

  return temp;
}


#
# queen_at()
#
# Determines whether or not there is a queen at a given location.
#
# Input:  rank, file
# Output: 1 (queen) or 0 (empty)
#

sub int queen_at (str board, int rank, int file) {
  var str temp;

#  print("Looking for queen at $rank, $file...\n");

  temp = at(board, rank, file);

  return 1 if (temp == "Q");
  return 0;
}


#
# free_space()
#
# Determines whether or not a space is free for placing a queen.
#

sub int free_space (str board, int rank, int file) {
  var int i = 1;

  while (i <= file) {
    var int temp_file;
    var int temp_rank;
    var int result;

    temp_file = file - i;

    temp_rank = rank;
    result = queen_at(board, temp_rank, temp_file);
    return 0 if (result == 1);

    temp_rank = rank + i;
    result = queen_at(board, temp_rank, temp_file);
    return 0 if (result == 1);

    temp_rank = rank - i;
    result = queen_at(board, temp_rank, temp_file);
    return 0 if (result == 1);

    i++;
  } 

  return 1;
}


#
# print_board()
#

sub print_board (str board) {
  var int rank, file;
  var int temp;

  rank = 7;

  sys::print("  +---+---+---+---+---+---+---+---+\n");

  while(rank >= 0) {
    temp = rank + 1;

    sys::print("$temp |");

    file = 0;
    while(file < 8) {
      var int result;

      result = queen_at(board, rank, file);

      if (result == 1) {
        sys::print(" Q |");
      } else {
        temp = rank + file;
        temp %= 2;
 
        if (temp == 1) {
          sys::print("   |");
        } else {
          sys::print(" * |");
        }
      }
      file++;
    }

    sys::print("\n");
    sys::print("  +---+---+---+---+---+---+---+---+\n");
    rank--;
  }

  sys::print("    A   B   C   D   E   F   G   H  \n");
}


#
# new_board()
#

sub str new_board()
{
  var str board = "";
  var int rank = 0;
  var int file = 0;

  sys::print("Making new board with $NUM_RANKS ranks and $NUM_FILES files...\n");

  while (rank < NUM_RANKS) {
    file = 0;
    while (file < NUM_FILES) {
      board = string::concat(board, " ");
      file++;
    }
    rank++;
  }

  var int l;
  l = string::length(board);
  sys::print("Board length is $l.\n");

  return board;
}


#
# main()
#

sub main() {
  var str board;
  var int rank;
  var int file;

  board = new_board();

  #
  # Scan over the files, placing queens:
  #

  file = 0;
  rank = 0;

  while (file < NUM_FILES) {
    while (rank < NUM_RANKS) {
      var int result;
      result = free_space(board, rank, file);
      last if (result == 1);
      rank++;
    }

    if (rank == NUM_RANKS) {
      file--;
      rank  = queen_rank(board, file);
      board = clear_file(board, file);
      rank++;
    } else {
      board = place_queen(board, rank, file);
      file++;
      rank = 0;
    }

    last if (file < 0);
  }

  #
  # Print the result:
  #

  print_board(board);
}


#
# MAIN PROGRAM:
#

main();

