import vector
import io
import random

type Iterator = {
    hasNext->Boolean
    get->T
    next->T}

type Vector = {
    size->Number
    isEmpty->Boolean
    clear->Void
    contains(_:T)->Boolean
    addFirst(_:T)->Void
    addLast(_:T)->Void
    firstElement->T
    lastElement->T
    removeFirst->T
    removeLast->T
    removeValue(_:T)->T
    indexOf(_:T)->Number
    at(_:Number)->T
    setValue(_:T)at(_:Number)->T
    add(_:T)at(_:Number)->Void
    removeFromIndex(_:Number)->T
    iterator->Iterator
    asString->String
    forEachDo(_:Block)->Void
    map(_:Block)->Vector
    ensureCapacity(_:Number)->Void
    capacity->Number
    copyInto(_)->Void
    indexOf(_:T)startingFrom(_:Number)->Number
    setSize(_:Number)->Void
    trimToSize->Void}

type TextCoinStrip = {
    asString->String
    isLegalMove(_:Number, _:Number)->Boolean
    makeMove(_:Number, _:Number)->Boolean
    gameIsOver->Boolean
    play->Void}

//===================================================

class TextCoinStripClass.new(sq:Number, c:Number){

    // the number of coins, 0 < coins < squares
    var coins:Number is readable := 
        if ((0 > c) || (c >= sq)) then {
            print "# coins: {c} must be positive and less than # squares: {sq}"
            print "setting # coins to 1"
            1}
        else{c}

    // the number of squares, 0 < coins < squares
    var squares:Number is readable :=
        if (coins >= sq) then {
           print "Game must be played with fewer coins than squares"
           print "setting # squares to 2"
           2}
        else{sq}

    // the strip of coins. A square is occupied by a coin if
    // the boolean value at thta location is true.
    var theStrip:Vector is private := vector.withSize(squares)
        increment(0)initialVal(false)

    theStrip.setSize(squares)
    
    var rand := random.generator
    while{0 < coins} do {
        var i:Number := rand.next(squares) + 1
        if(!theStrip.at(i)) then{
            theStrip.setValue(true)at(i)
            coins := coins - 1}
    }

    // @return string representation of a strip
    method asString->String is public{
        var rep:String := ""
        for(1..theStrip.size) do {i->
            if(theStrip.at(i)) then {rep := rep ++ "o"}
            else{rep := rep ++ "_"}
        }
        return rep}

    // @param start: location of coin to be moved
    // @param distance: how far coin is to move
    // @return true if move is legal
    method isLegalMove(start:Number, distance:Number)->Boolean is public{
        var legal:Boolean := true
        if((start > theStrip.size) || (start < 1) || (distance < 1)) then {return false} 
        if(!theStrip.at(start) ||
            ((start - distance) <= 0)) then{
                return false}
        for(1..distance) do {i->
            if(theStrip.at(start - i)) then {return false}
        }
        return legal}

    // makes a (legal) move
    // @param start: the location of the coin to be moved
    // @param distance: how far the coin is to move
    // @pre the move msut be legal
    method makeMove(start:Number, distance:Number)->Boolean is public{
        theStrip.setValue(false)at(start)
        theStrip.setValue(true)at(start - distance)}

    // determines if game is completed
    // @return true if there are no more moves
    method gameIsOver->Boolean is public{
        var over:Boolean := true
        var numcoins:Number := 0
        for (1..theStrip.size) do {i->
            if(theStrip.at(i)) then {numcoins := numcoins + 1}
        }
        for (1..numcoins) do {i->
            if(!theStrip.at(i)) then {over := false}     
        }
        return over}

    // plays the Silver Dollar Game until it is finished
    method play is public{
        var istream := io.input
        var start:Number := 0
        var distance:Number := 0
        while {!gameIsOver} do {
            print(asString ++ " Next move ?")
            var soFar := ""
            var chr := istream.next
            while{chr != "\n"} do {
                soFar := soFar ++ chr
                chr := istream.next}
            start := soFar.asNumber.truncate
            soFar := ""
            chr := istream.next
            while{chr != "\n"} do {
                soFar := soFar ++ chr
                chr := istream.next}
            distance := soFar.asNumber.truncate
            if(isLegalMove(start, distance)) then {
                makeMove(start, distance)}
            else{print "Illegal move!"}
        }
        print(asString ++ " You win")}

}

//=================================================

// demonstration of game; constructs 12-square strip
// with 5 coins and plays Silver Dollar Game

var tcs:TextCoinStrip := TextCoinStripClass.new(12, 5)
tcs.play