Κυριακή 17 Μαΐου 2020

Dutch national flag problem

Revision 25 uploaded. A bug fixed in sorting a tuple with two items.


The Dutch national flag problem, introduced by Edsger W. Dijkstra. We have three colored type balls, one for each color from Dutch flag. We have to get a souffle of balls and then we have to make the minimum swaps to set the balls according to flag, Red then White then Blue balls.

The setup file of M2000 in the info.gsb file a Dutch module exist for fun. Here we will see the code, and how we can compute the changes before we did the swaps. We have a tuple which get a series of constant values, from an enumeration type named balls.

enum balls {Red, White, Blue}

We can set a variable with one of the three constants, say x=Red and we can read the name of constant using Eval$(x). The type of x is balls, so if we Print Type$(x) we get balls. The x value is not constant but if we place a number which didn't exist in the balls type then we get error. Red has value 1 by default. We can change this in the definition using Red=100 so the next one get 101 or we have to set the value using White=200 and so on.So the value of the constant can be used as a number. Internal is an object. So here we use arrays of one dimension which like all arrays in M2000 have a variant type and only the name add a restriction when we extract a value or place a value. A tuple has no name, so (White,) is one item tuple (look the comma after White), ot (,) is the empty tuple, or (Red, White) is a tuple with two values. We can fold other tuple as items of tuple. so ((1,2),(3,4)) is a tuple of two tuple. From tuple is easy to get a value, but we have to use a Return obj statement to return values to multiple indexes on a tuple. Or we can link a tuple to a name, an array name, so if variable a is a pointer to a tuple then a Link a to a() make a() the same array and we can strore and read values like in an array. We can't assign second time a  reference to a name (a Link is a reference), But we can make temporary names in a For obj { } block, so we can make for sort period a name as a reference and we can use this block several times to link different tuples. An array with a name with parenthesis has the same object at the bottom as the tuple, but used as a different interface. So a A()=B() make a shallow copy of B() to A(), but a pointer of a tuple just copy the pointer. Also another difference is that a pointer to an array use the array as one dimension, from 0. Arrays with names and parenthesis may have up to 10 dimension and we can set the lower an upper limit (negatives included). So let see what we have here in the Dutch National Flag Problem

We can make some lambda functions. These functions also may have closures. Here in fillarray we place a as a pointer to a tuple with three constants. Using another lambda inside the fillarray, we pass the a as closure so each time we use randomitem we get one value at random fro there. To finish it quickly we make an array a(size) using the << operator which execute the right function for each item. So we make the random array of size size, and then we place it as the return value, using = as a statement (get blue color when it is a statement, the M2000 editor is smart enough to know when this symbol isn't an operator but a statement).

fillarray=lambda a=(Red, White, Blue) (size as long=10)-> {
    if size<1 then size=1
    randomitem=lambda a->a#val(random(0,2))
    dim a(size)<<randomitem()
    =a()
}

We want to display an array so we make another lambda function: The $ symbol used every time we need a name to return a string value. So this function is a string function. We can use a normal function, but for fun we make it a lambda function. See that we check if a is an array. We can through the check, but sometimes is good to catch errors. The if then/end if is a variant of if  then { } structure. The for loop in m2000 always execute at least one time. The direction can be change if the last value is lower than the first one

Display$=lambda$ (s as array) ->{
    Document r$=eval$(array(s))
    if len(s)>1 then
    For i=1 to len(s)-1 {
        r$=", "+eval$(array(s,i))
    }
    end if
    =r$
}

We use a document object. This is like a string, when we use in string expressions. But we can use specific statements with this for some document tasks. Inside document object each paragraph is a separate string. So appending a string has no big cost.  The = operator works like append. To clear a document we use Clear statement. We see in above code that we use array(s, i) to get value from s at index i, the same can be done using s#val(i).

We need to check if an array is sorted or not so we make another function, which return a string. Also it is a lambda function, for fun. We use two times the return of a value. In a function the return statement = is optional (so we return 0 or empty string according the name of the function), and we can feed it more than one (replacing the last one). To exit from a function, we have to execute until the end of block, or exit from block. Here we use break, which breaks all blocks until the function block. We get a value, then we start a loop to check each k if x>k, to break it. A for loop in M2000 always execute one time (or half if a break or exit happen). If we place one item array we get an error, because we have to use two items. A test for one item has no meaning. A statement: if len(s)<1 then error "not used here"  can be used here but a function has to call with the right parameters, so the caller is responsible to make the right call.

TestSort$=lambda$ (s as array)-> {
    ="unsorted: "
    x=array(s)
    for i=1 to len(s)-1 {
        k=array(s,i)
        if x>k then break
        swap x, k
    }
    ="sorted: "
}

So now we need another function to get an array of the final position from the unsorted array. If a three item array (three balls for this context) was sorted before entering the Positions() function we get (0, 1, 2) where we read the 0 final position need to get from 0 (no swap), we read the 1 final position need to get from 1 and the final third ball is the third from original. So we have no swaps.

We use a stack object (has a collection deep inside), which we use no keys. We can easy adding to both ends (we say the top and the bottom). The basic idea to extract the positions has two values low and high, so at the end these separate the balls, to chunks. The function has to work when one or two kinds of balls just non included. When we found the ball for middle zone on flag,  we place it at the bottom of the stack using Data. When the loop ends, we have to get the items on stack converted to array. So c()=array(medpos) where medpos is a stack,  is an array with all the balls for middle color, the White. There is a function array() and for a parameter a pointer to a stack object move the items to an array, and empty the stack. Also we use a Stock statement to copy an array to another array, from two indexes and for a number of items.

So the function Positions return an array of Positions, from where we get an item. So if in index=0 we have a 5 that means that for a destination array in index 0 we copy the item from index 5 from the source array.
Positions=lambda mid=White (a as array) ->{
    m=len(a)
    dim Base 0, b(m)=-1
    low=-1
    high=m
    m--
    i=0
    medpos=stack
    link a to a()
    for i=m to 0 {
        if a(i)<=mid then exit
        high--
        b(high)=high
    }
    for i=0 to m {
        if a(i)>=mid then exit
        low++
        b(low)=low
    }
    if high-low>1 then
    for i=low+1 to high-1 {
        select case a(i)<=>Mid
        case -1
            low++ : b(low)=i
        case 1
        {
            high-- :b(high)=i
            if High<i then swap b(high), b(i)
        }
        else case
            stack medpos {data i}
        end select
    }
    end if
    if Len(medpos)>0 then
    dim c()
    c()=array(medpos)
    stock c(0) keep len(c()), b(low+1)
    for i=low+1 to high-1
           if b(i)>low and b(i)<high and b(i)<>i then swap b(b(i)), b(i)    
    next i
    end if
    if low>0  then
        for i=0 to low
            if b(i)<=low and b(i)<>i then swap b(b(i)), b(i)
        next 
    end if
    if High<m then
        for i=m to High
            if b(i)>=High and b(i)<>i then swap b(b(i)), b(i)
        next 
    end if
    =b()
}

So we can make a new array for flags just using the positions. One easy way is to copy the source array to destination and then scan the positions where index <> value, because if index=value that means that the item stay to the same position for source and destination arrays.

Another way is to make the changes in place. And that is the goal of this program.
To do that we need another function, the InPlace function. Here we place two arrays by reference. the position and the Final() which is the source and the destination array. When we have a position equal to index then we skip that. But when we have a position to place we have the information of the source item, so we keep the first item in z and place all the items until we find the same index again as position to copy and here we finish the inner swap multi action. Maybe there are more than one or no one inner swap actions. The InPlace function also return the counting of swaps

InPlace=Lambda (&p(), &Final()) ->{
    def i=0, j=-1, k=-1, many=0
    for i=0 to len(p())-1
        if p(i)<>i then
            j=i
            z=final(j)
            do
                final(j)=final(p(j))
                k=j
                j=p(j)
                p(k)=k
                many++
            until j=i
            final(k)=z
        end if
    next
    =many
}

This is the final program (as in Dutch module, in info.gsb, included file in M2000 setup, from revision 25, version 9.9):
Added the Three Way Partition from https://en.wikipedia.org/wiki/Dutch_national_flag_problem
Sometimes is better than the first algorithm. If the array is already sortec the first algorithm make no swaps. The second make a lot of swaps.

Report "Dutch Flag from Dijkstra"
const center=2
enum balls {Red, White, Blue}
fillarray=lambda a=(Red, White, Blue) (size as long=10)-> {
    if size<1 then size=1
    randomitem=lambda a->a#val(random(0,2))
    dim a(size)<<randomitem()
    =a()
}
Display$=lambda$ (s as array) ->{
    Document r$=eval$(array(s))
    if len(s)>1 then
    For i=1 to len(s)-1 {
        r$=", "+eval$(array(s,i))
    }
    end if
    =r$
}
TestSort$=lambda$ (s as array)-> {
    ="unsorted: "
    x=array(s)
    for i=1 to len(s)-1 {
        k=array(s,i)
        if x>k then break
        swap x, k
    }
    ="sorted: "
}
Positions=lambda mid=White (a as array) ->{
    m=len(a)
    dim Base 0, b(m)=-1
    low=-1
    high=m
    m--
    i=0
    medpos=stack
    link a to a()
    for i=m to 0 {
        if a(i)<=mid then exit
        high--
        b(high)=high
    }
    for i=0 to m {
        if a(i)>=mid then exit
        low++
        b(low)=low
    }
    if high-low>1 then
    for i=low+1 to high-1 {
        select case a(i)<=>Mid
        case -1
            low++ : b(low)=i
        case 1
        {
            high-- :b(high)=i
            if High<i then swap b(high), b(i)
        }
        else case
            stack medpos {data i}
        end select
    }
    end if
    if Len(medpos)>0 then
    dim c()
    c()=array(medpos)
    stock c(0) keep len(c()), b(low+1)
    for i=low+1 to high-1
           if b(i)>low and b(i)<high and b(i)<>i then swap b(b(i)), b(i)    
    next i
    end if
    if low>0  then
        for i=0 to low
            if b(i)<=low and b(i)<>i then swap b(b(i)), b(i)
        next 
    end if
    if High<m then
        for i=m to High
            if b(i)>=High and b(i)<>i then swap b(b(i)), b(i)
        next 
    end if
    =b()
}
InPlace=Lambda (&p(), &Final()) ->{
    def i=0, j=-1, k=-1, many=0
    for i=0 to len(p())-1
        if p(i)<>i then
            j=i
            z=final(j)
            do
                final(j)=final(p(j))
                k=j
                j=p(j)
                p(k)=k
                many++
            until j=i
            final(k)=z
        end if
    next
    =many
}


Dim final(), p(), second(), p1()
Rem final()=(White,Red,Blue,White,Red, Red, Blue)
Rem final()=(white, blue, red, blue, white)


final()=fillarray(30)
Print "Items: ";len(final())
Report TestSort$(final())+Display$(final())
\\ backup for final() for second example
second()=final()
p()=positions(final())
\\ backup p() to p1() for second example
p1()=p()




Report Center,  "InPlace"
rem Print p()   ' show array items
many=InPlace(&p(), &final())
rem print p()  ' show array items
Report TestSort$(final())+Display$(final())
print "changes: "; many


Report Center, "Using another array to make the changes"
final()=second()
\\ using a second array to place only the changes
item=each(p1())
many=0
While item {
    if item^=array(item) else final(item^)=second(array(item)) : many++
}
Report TestSort$(final())+Display$(final())
print "changes: "; many

Module three_way_partition (A as array, mid as balls, &swaps) {
    Def i, j, k
    k=Len(A)
    Link A to A()
    While j < k
        if A(j) < mid Then
            Swap A(i), A(j)
            swaps++
            i++
            j++
        Else.if A(j) > mid Then
            k--
            Swap A(j), A(k)
            swaps++
        Else
            j++
        End if
    End While
}
Many=0
Z=second()
Print
Report center, {Three Way Partition
}
Report TestSort$(Z)+Display$(Z)
three_way_partition Z, White, &many
Print
Report TestSort$(Z)+Display$(Z)
Print "changes: "; many


Παρασκευή 15 Μαΐου 2020

Το ταξίδι του Αλόγου (Knight's Tour)

Υπενθύμιση: έχει ανέβει η αναθεώρηση 24 με μικρές διορθώσεις πάνω στο διερμηνευτή (λεπτομέρειες δηλαδή).

Ακολουθεί το πρόγραμμα knight Tour, όπου στόχος είναι να δίνουμε μια θέση σε μια άδεια σκακιέρα και να μετακινήσουμε ένα άλογο σε όλες τις θέσεις χωρίς να περάσουμε δεύτερη φορά από μια ίδια θέση. Υποχρεωτικά πρέπει να χρησιμοποιήσουμε BackTrack, να παίρνουμε πίσω κινήσεις αν δεν βγαίνει το μέτρημα. Το μέτρημα είναι απλό, βάζουμε 1 στη αρχική θέση και αυξάνουμε μια μεταβλητή Moves ή αν χρειαστεί την μειώνουμε και σβήνουμε βάζοντας μια μεγάλη τιμή πχ το 100 αρκεί.
Το ζήτημα είναι ότι αν δούλευε μόνο αυτό θα έπρεπε κάποτε να περιμένουμε χρόνια. Οι συνδυασμοί είναι 19,591,828,170,979,904. Οπότε πρέπει κάτι να γίνει πριν ξεκινήσει η τοποθέτηση του αλόγου, να έχουμε προετοιμάσει το έδαφος!

Χρειαζόμαστε μια συνάρτηση όπου θα δίνουμε τη τρέχουσα θέση του αλόγου και έναν αριθμό θέσης, από το 1 έως το 8. Αν το άλογο είναι σε κάποια θέση στο κέντρο της σκακιέρας υπάρχουν και οι οκτώ θέσεις. Αν είναι  όμως σε γωνία υπάρχουν μόνο δυο θέσεις. Έτσι η συνάρτηση μπορεί να γυρίσει το ζεύγος της νέας θέσης ή κενό. Το κενό εδώ είναι ο κενός πίνακας, ή κενό tuple. Ο κενός πίνακας ως σταθερά, στη Μ2000 συμβολίζεται με (,)  (δείτε το κόμμα ανάμεσα στις παρενθέσεις).

Την KnightMove() την βάζουμε τοπική συνάρτηση στη τελική όπως θα δούμε στο τέλος.
Δείτε ότι παίρνει τρεις μεταβλητές, η x και η αρχική θέση του αλόγου όπου το w=1 και h=1 είναι η θέση a1, η κάτω αριστερή γωνία από την μεριά των άσπρων.
Η συνάρτηση χρησιμοποιεί τέσσερις μεταβλητές. Αν ο x είναι μονός τότε αλλάζουμε τα a και b, μεταξύ τους. Αν το x είναι μεγαλύτερο του 2 αλλάζουμε πρόσημο στο p, και συνεχίζουμε στην επόμενη If (αν η πρώτη δεν βγει αληθής δεν συνεχίζει η ροή στις επόμενες). Αν είναι το x μεγαλύτερο του 4 τότε αλλάζουμε τα z και p (άρα το z γίνεται -1 και το p γίνεται 1). Αν το x είναι μεγαλύτερο του 6 τότε κάνουμε και το p αρνητικό. Μετά προσθέτουμε τα z*a και p*b στα w και h. Οι τυπικές παράμετροι περνούν με τιμή στην Μ2000 εκτός αν βάλουμε το & στην αρχή του ονόματος και στην κλήση δώσουμε επίσης μεταβλητή με το & στην αρχή (αυτό είναι το πέρασμα με αναφορά).
Στο τέλος ελέγχουμε αν η θέση που θα πάει το άλογο είναι εκτός σκακιέρας. Αν δεν είναι εκτός τότε δίνουμε το ζεύγος τιμών (w, h) αλλιώς δίνουμε το κενό (,)

Function KnightMove(x,w,h) {
    a=2:b=1:z=1:p=1
    if x mod 2=1 then swap a,b
    if x>2 then p-! : if x>4 then swap z, p : if x>6 then p-!
    w+=z*a
    h+=p*b
    if w>=1 and w<=8 and h>=1 and h<=8 then =(w, h) else =(,)
}

Παρατηρούμε ότι το x δεν λειτουργεί κυκλικά, κάπου δηλαδή πηδάει τεταρτημόριο, το οποίο το συνεχίζει αργότερα. Κάνει μια κίνηση Ζήτα, 1ο, 2ο, 4ο, 3ο.

Το πρόγραμμα βγάζει τις θέσεις του αλόγου στη σκακιέρα
Knight's Tour from a1
a1->b3->a5->b7->d8->f7->h8->g6->
h4->g2->e1->c2->a3->b1->d2->f1->
h2->g4->h6->g8->e7->c8->a7->b5->
c7->a8->b6->a4->b2->d1->f2->h1->
g3->h5->g7->e8->f6->h7->f8->d7->
b8->a6->b4->a2->c1->e2->g1->h3->
g5->e6->f4->d3->c5->e4->c3->d5->
e3->c4->d6->f5->d4->f3->e5->c6
Knight's Tour from h1
h1->g3->h5->g7->e8->c7->a8->b6->
a4->b2->d1->f2->h3->g1->e2->c1->
a2->b4->a6->b8->d7->f8->h7->g5->
f7->h8->g6->h4->g2->e1->c2->a1->
b3->a5->b7->d8->c6->a7->c8->e7->
g8->h6->g4->h2->f1->d2->b1->a3->
b5->d6->c4->e3->f5->d4->f3->e5->
d3->f4->e6->c5->e4->c3->d5->f6
Knight's Tour from a8
a8->b6->a4->b2->d1->f2->h1->g3->
h5->g7->e8->c7->a6->b8->d7->f8->
h7->g5->h3->g1->e2->c1->a2->b4->
c2->a1->b3->a5->b7->d8->f7->h8->
g6->h4->g2->e1->f3->h2->f1->d2->
b1->a3->b5->a7->c8->e7->g8->h6->
g4->e3->f5->d6->c4->e5->c6->d4->
e6->c5->d3->f4->d5->f6->e4->c3
Knight's Tour from h8
h8->g6->h4->g2->e1->c2->a1->b3->
a5->b7->d8->f7->h6->g8->e7->c8->
a7->b5->a3->b1->d2->f1->h2->g4->
f2->h1->g3->h5->g7->e8->c7->a8->
b6->a4->b2->d1->c3->a2->c1->e2->
g1->h3->g5->h7->f8->d7->b8->a6->
b4->d3->c5->e6->f4->d5->f6->e4->
d6->f5->e3->c4->e5->c6->d4->f3


Για τη διευκόλυνσή μας, βελτιστοποιούμε τον αλγόριθμο για ένα 4Χ4 τμήμα όπου μπαίνει το άλογο. Αν επιλέξουμε ένα άλλο τότε το γυρνάμε στο συμμετρικό του (κατά ύψος και κατά πλάτος) . Παρατηρήστε το αποτέλεσμα παραπάνω. Σε κάθε περίπτωση ο αλγόριθμος υπολόγισε τις θέσεις από την ίδια θέση! Απλά έκανε αλλαγή στα ονόματα και στους αριθμούς και έτσι πήραμε το σωστό "καθρέφτισμα".

Ο αλγόριθμος κρατάει έναν πίνακα Moves() που έχει τιμές από το (1,1) ως το (8,8). Όταν τελειώσει το γέμισμα υπάρχουν οι αριθμοί από το 1 έως το 64. Σαρώνουμε το πίνακα και φτιάχνουμε μια κατάσταση όπου το κλειδί είναι ο αριθμός και η τιμή είναι το a1 (αν έχουμε το 1,1). Το κάνουμε αυτό για να δώσουμε μια εντολή ταξινόμησης κλειδιού ως αριθμός. Οπότε μετά την ταξινόμηση περνάμε από όλα τα στοιχεία, από το 1 έως το 64. Και έτσι φτιάχνουμε την εξαγωγή παραπάνω. Χρησιμοποιώ ένα έγγραφο το export$ το οποίο αν και μοιάζει με αλφαριθμητικό εσωτερικά έχει ένα αντικείμενο το οποίο κρατάει χωριστά τις παραγράφους, με συνέπεια να μπορούμε προσθέτουμε πολύ εύκολα παραγράφους. Το σύμβολο = δεν κάνει εκχώρηση τιμής στο έγγραφο αλλά προσθήκη. Για να σβήσουμε το έγγραφο πρέπει να χρησιμοποιήσουμε το Clear ή Καθαρό, ή να φορτώσουμε ένα Έγγραφο από το δίσκο (μπορούμε να κάνουμε και προσθήκη κειμένου από αρχείο με μια εντολή).
Στην επιστροφή της KnightTour$() επιστρέφουμε το έγγραφο ως αλφαριθμητικό (το αντικείμενο διαγράφεται εσωτερικά).

Αυτό είναι το κύριο πρόγραμμα. Φτιάχνει ένα δικό του έγγραφο και ρίχνει μια έκφραση αλφαριθμητική (σε πολλές γραμμές). Το { } χρησιμοποιείται εκτός από μπλοκ εντολών και για αλφαριθμητικά με παραγράφους. Εδώ μας ενδιαφέρουν οι αλλαγές γραμμών. Δείτε ότι στην έκφραση καλούμε τέσσερις φορές το KnightTour$(). Την πρώτη φορά δεν βάζουμε τιμές. Εξ ορισμού έχουν από 1 η κάθε μία. Στη Μ2000 αν δεν βάλουμε τιμή σε μεταβλητή θα βγει λάθος. Στους πίνακες δεν ισχύει αυτό, γιατί μπαίνει τιμή εξ ορισμού η Empty (χωρίς τιμή) η οποία μπορεί να διαβαστεί και σαν 0 ή σαν "" (κενό αλφαριθμητικό). Στις μεταβλητές ο τύπος δεν αλλάζει οπότε χρειάζεται να έχει τύπο, δηλαδή να πάρει τιμή. Μόνο σε ειδικές εντολές ορισμού μεταβλητών αν θέλουμε δεν δίνουμε τιμή, και εκεί μπαίνει η εξ ορισμού. Ο τύπος εξ ορισμού είναι ο Double για αριθμητικές. Τα αλφαριθμητικά είναι String.

Στο κύριο πρόγραμμα  έχουμε δυο εντολές στο τέλος. Μια γράφει στο πρόχειρο το κείμενο του εγγράφου και μια άλλη το εμφανίζει στην οθόνη (κονσόλα) με έλεγχο ολίσθησης, όπου κάθε 3/4 του ύψους της κονσόλας ολίσθησης περιμένει για πάτημα διαστήματος ή πλήκτρου ποντικιού.

Document ex$
ex$= {Knight Tour from a1
}+ KnightTour$()+{Knight Tour from h1
}+ KnightTour$(8,1)+{Knight Tour from a8
}+KnightTour$(1, 8)+{Knight Tour from h8
}+KnightTour$(8, 8)
Clipboard ex$
Report ex$

Ας δούμε σημεία του KnigtTour$()
Υπάρχει ένας δεύτερος πίνακας το Board() πάλι από (1,1) έως (8,8), αλλά εδώ μας ενδιαφέρει σε κάθε στοιχείο να βάλουμε ένα στοιχείο Σωρός (Stack). 
Επίσης υπάρχει το f, ένας σωρός (ειδική στοίβα) με τιμές 1,2,3,4,5,6,7,8 οι οποίες όμως μπορούν να μετατοπιστούν και για το λόγο αυτό χρησιμοποιούμε το σωρό. Η stackitem(f, k) διαβάζει από το αντικείμενο f το k στοιχείο (ως αριθμό, αν είναι αλφαριθμητικό θα πάρουμε σφάλμα, οι σωροί παίρνουν τα πάντα ακόμα και άλλους σωρούς).

Έχουμε ένα τριπλό For (με δυο φωλιασμένα) τα οποία χρησιμοποιούμε με την απλοποίηση χωρίς αγκύλες, με το τέλος να το δηλώνει το Next. Εδώ δεν βάζουμε στη Next την αντίστοιχη μεταβλητή (αν την  βάλουμε την ελέγχει ο διερμηνευτής).
H s μέσα στο πρώτο φωλιασμένο for, παίρνει έναν νέο σωρό. Οι μεταβλητές που κρατάνε έναν καταχωρητή στοιχείων όπως ο σωρός είναι δείκτες σε αντικείμενα. Αυτό σημαίνει ότι αν τους περάσουμε με τιμή σε μια κλήση, στην ουσία το αντικείμενο είναι εκτεθειμένο σε αλλαγές, εκτός από την αλλαγή του δείκτη (αν συμβεί αλλαγή δείκτη τότε το αρχικό θα δείχνει ακόμα αυτό που πέρασε στη κλήση). Επιπλέον μπορούν δυο ή περισσότεροι δείκτες να δείχνουν το ίδιο αντικείμενο. Επίσης μπορούμε να αποθηκεύσουμε το δείκτη σε πίνακα. Πάμε λοιπόν στο δεύτερο φωλιασμένο For. Εκεί καλούμε την KnightMove με τη τιμή που μας δίνει ο σωρός f για το k, και τις τιμές i και j. Την επιστροφή την γράφουμε σε μια μεταβλητή m. Εδώ θα πάρουμε ή έναν πίνακα με δυο στοιχεία (δηλαδή μήκος ως μονοδιάστατου 2), ή έναν κενό (μήκος=0). Για το λόγο αυτό κοιτάμε αν το Len(m) είναι>1 (και μεγαλύτερο του 0 θα έπαιζε). Τότε αν έχουμε πάρει όντως μια θέση (ζευγάρι dyo τιμών) στη σκακιέρα την προσθέτουμε στο stack s. Η δομή Stack δείκτης_σε_σωρό { } βάζει τον σωρό s ως τρέχον σωρό και αποκρύπτει τον μέχρι τώρα τρέχον σωρό. Στην έξοδο επιστρέφει ο τρέχον σωρός του τμήματος. Έτσι μέσα στις αγκύλες, η Data m προσθέτει στο τέλος του σωρού το m - λειτουργία FIFO, το διάβασμα γίνεται από τη κορυφή, η Push βάζει στη κορυφή, η Data στο πυθμένα, ή τέλος).
Δείτε τώρα τι κάνουμε μετά τη καταχώρηση των έως 8 θέσεων στο s, το γράφουμε στο Board(i,j). Έτσι ο δείκτης του σωρού πάει εκεί (ένας σωρός με πίνακες), Και σε κάθε αλλαγή οριζόντιας γραμμής (μετά το δεύτερο Next), κάνουμε αντιστροφή των στοιχείων του f, αυτό γίνεται μόλις βάλουμε το f στη θέση του τρέχον και δώσουμε την εντολή Shift 1, -8, το οποίο βάζει τα στοιχεία ανάποδα (πχ αν είχαν την καλή σειρά θα πάρουν την ανάποδη 8,7,6,5,4,3,2,1).

For j=1 to 8 :For i=1 to 8
s=stack
For k=1 to 8
    m=KnightMove(stackitem(f, k),i, j)
    if len(m)>1 then Stack s {data m}
   Next : Board(i,j)=s : Next
  stack f {shift 1,-8}
Next

Τώρα έχουμε ένα πίνακα Board() όπου σε κάθε (i,j) θέση αντιστοιχούν μια σειρά θέσεων που μνορεί να πάει το άλογο. Έτσι δεν χρειάζεται να χρησιμοποιούμε την συνάρτηση πια. Έχουμε έτοιμες τις επόμενες θέση για κάθε θέση στη σκακιέρα. Ακόμα όμως δεν είμαστε καλοί!

Θέλουμε όταν εξετάζουμε μια θέση να κοιτάμε πρώτα τις θέσεις που είναι στις άκρες και τις πλευρές, όπου αυτές έχουν λιγότερες από 8 θέσεις προορισμού σε σχέση με τις κεντρικές θέσεις.
Τώρα που ο πίνακας Board() έχει για κάθε θέση από μια λίστα θέσεων προορισμού μπορούμε να κάνουμε ταξινόμηση ώστε κάθε ζεύγος στο σωρό να μπει βάσει του αριθμού στοιχείων που έχει αν το ζεύγος αυτό δοθεί στον πίνακα Board().

Δείτε τι κάνουμε: Πρώτα παίρνουμε αντίγραφο του δείκτη στο Board(i,j) στο s. Αν έχουμε δυο στοιχεία δεν τα ταξινομούμε! Είναι οι γωνίες. Μόνο οι γωνίες έχουν δυο θέσεις για προορισμό ισοδύναμε σε ότι έχει να κάνει με τις θέσεις του κάθε προορισμού σε προορισμούς!
Μετά φτιάχνουμε μια ουρά! Η ουρά (queue) στη Μ2000 είναι μια ειδική κατάσταση. Εδώ όταν ταξινομούμε τα στοιχεία της δεν έχουμε αλλαγή θέσεων όταν το κλειδί είναι ίδιο. Επίσης, μόνο σε αυτόν τον τύπο κατάστασης μπορούμε να έχουμε όμοια κλειδιά (η κανονική κατάσταση λέγεται και λίστα έχει μοναδικά κλειδιά. και αν πάμε να βάλουμε ίδιο βγαίνει λάθος).
Διαβάζουμε λοιπόν όλα τα k στοιχεία του s με την stackitems)s, k) (οι σωροί ξεκινούν από το 1), στο m. Βάζουμε στο τέλος της ουράς με την Append, με κλειδί το μήκος Len() του στοιχείου της Board() όπου οι θέσεις βγαίνουν από τις τιμές στο 0 και στο 1 (τα touple έχουν βάσει το 0).. Ο αριθμός θα γίνει κλειδί στην ουρά, και τιμή θα πάρει ένα αντίγραφο του δείκτη m. Μόλις τελειώσει ο εμπλουτισμός της ουράς κάνουμε τη ταξινόμηση με χρήση του κλειδιού ως αριθμός. Αν είχαμε κλειδιά πχ Α50 και Α100 το Α50 θα έρχονταν πρώτο, ενώ αν ήταν ταξινόμηση ως κείμενο θα έρχονταν δεύτερο αφού πάει ανά χαρακτήρα ο έλεγχος. Στη ταξινόμηση ως αριθμός τα κλειδιά σπάνε σε μέρη αλφαριθμητικά και αριθμητικά. Εδώ έχουμε αριθμητικό (ταξινομεί ακόμα και με δεκαδικά σωστά). Τα κλειδιά ακόμα και τα αριθμητικά γίνονται αλφαριθμητικά στην ουρά.
Μετά την ταξινόμηση φτιάχνουμε ένα νέα σωρό στην s, και με μια Stack s { } με μια For Next μέσα βάζουμε τα στοιχεία με την data. Δείτε ότι την ουρά την διατρέχουμε εδώ με αριθμό k από το 0 και στο so() βάζουμε το ! που δηλώνει ότι δίνουμε αριθμό σειράς και όχι κλειδί. Οι λίστες και οι ουρές, οι δυο Καταστάσεις, έχουν πίνακα κατακερματισμού εσωτερικά για να βρίσκουν άμεσα σε O(1) το ζητούμενο ανεξάρτητα με το μέγεθος σε στοιχεία της δομής. Οι ουρές για λίγα στοιχεία έχουν ικανοποιητική ταξινόμηση και διατηρούν την σειρά στα όμοια κλειδιά, ενώ η λίστες έχουν quicksort, πολύ γρήγορη, δεν έχουν όμοια κλειδιά).

For i=1 to 8 :For j=1 to 8
s=Board(i, j)
if len(s)>2 then
    so=queue
    For k=1 to len(s)
        m=stackitem(s, k)
        Append so, Len(Board(m#val(0), m#val(1))) :=m
    Next
    sort ascending so as number
    s=stack
    stack s {for k=0 to len(so)-1:data so(k!):next}
    Board(i,j)=s
end if
Next : Next

Τώρα έχουμε την Board() με ταξινομημένους σε κάθε θέση τους προορισμούς, από τις θέσεις με λίγους προορισμούς προς τις θέσεις με τους πολλούς προορισμούς. Θέσεις με ίδιο αριθμό προορισμών δεν έχουν αλλάξει σειρά.

Πριν μπούμε στην βασική ή κεντρική επανάληψη, πρέπει να καθορίσουμε αρχικές συνθήκες. Πρώτα στην s θα πάρουμε τον σωρό (τις θέσεις προορισμού) από το σημείο (StartWStartH), το αρχικό σημείο. Έχουμε την n με μηδέν τιμή (θα δούμε γιατί έχει μηδέν μετά).
Έχουμε επίσης την BackTrack ένα σωρό για να κρατάμε τις θέσεις που περνάμε. Το πώς θα το δούμε πιο μετά. Η Moves ξεκινάει από το 1, και βάζουμε στο Moves() στην πρώτη θέση το 1.

s= Board(StartW, StartH)
n=0
BackTrack=Stack
Moves=1
Moves(StartW, StartH)=1

Τώρα είμαστε έτοιμοι να ξεκινήσουμε. Μπαίνουμε σε μια Repeat Until μέχρι η Moves να γίνει final (το 64 για 8*8 σκακιέρα). Δείτε οτι η πρώτη εντολή αυξάνει κατά ένα την n. Θυμηθείτε ότι τους σωρούς μέσα στην Board() τους διαβάζουμε από το 1. Επίσης έχουμε ήδη την s με το περιεχόμενο της πρώτης θέσης (θέση εκκίνησης του αλόγου).

Repeat
n++
------
until Moves=final

Μετά την n++ θέλει προσοχή. Έχουμε μια while με συνθήκη το n>len(s). Αυτό θα συμβεί όταν έχουμε πάει σε όλους τους προορισμούς και δεν βρήκαμε άδεια θέση. Δεν υπάρχει περίπτωση να είμαστε στην 64 θέση και να περάσουμε από εκεί γιατί θα έχουμε βγει από την Repeat Until.
Μετά θα πάρουμε τη θέση στο n από το σωρό s, στην m (που είναι δείκτης σε πίνακα). Θα εξάγουμε τα w και h, τις θέσεις, και θα δούμε αν η Moves(w,h) έχει τιμή μεγαλύτερη από την Moves. (βάζουμε το 100 για να σημαίνει άδειο στην αρχή).  Αν δεν βρούμε κενή θέση στο (w, h) τότε πάμε για άλλο n, το επόμενο. 

Repeat
n++
While n>len(s) {
.................
}
m=stackitem(s, n)
w=m#val(0)
h=m#val(1)
if Moves(w, h)>=Moves then
--------------
end if
until Moves=final

Ήρθε η ώρα να δούμε την πρώτη περίπτωση, ότι η θέση υπάρχει στο (w,h). Έχουμε μια If Then Else End If όπου ελέγχουμε αν η Moves είναι μικρότερη από τη last. H last είναι η προηγούμενη από την final, η 63. Αν είμαστε στην 63 θα πάμε στο δεύτερο σκέλος, μετά την else, θα κάνουμε την moves 64 και έχουμε τελειώσει γιατί θα βγούμε μετά από το  

if Moves(w, h)>=Moves then
    if Moves<last then
         -----
    else
        Moves++
        Moves(w,h)=Moves
    end if
end if

Τώρα μας ενδιαφέρει τι κάνουμε στο Moves<last. Θυμάστε ότι από το s πήραμε αντίγραφο του δείκτη στο m από τη θέση n με την stackitem(s,n) και από το m βγάλαμε τα w και h, όπου σε αυτό το σημείο βρήκαμε ότι έχουμε άδεια θέση. Πάμε λοιπόν να δούμε τι έχουμε στο Board(w, h). Πρώτα θα πάρουμε αντίγραφο δείκτη στο s1. και ξεκινάμε με ένα ii=-1
Μπαίνουμε σε μια επανάληψη For (εδώ έχουμε τις αγκύλες) για κάθε i από 1 έως μήκος του s1.
Εντός της For βγάζουμε για κάθε i στο s1 ένα δείκτη m1 θέσης προορισμού. Κοιτάμε αν στη θέση αυτή έχουμε κενό, και αν ναι τότε βάζουμε στο ii το i-1. δηλαδή αν το βρούμε στη θέση 1 του s1, θα βάλουμε στο ii=0. Αν δεν βρούμε καμία θέση τότε θα μείνει το ii με την αρχική τιμή -1. Δείτε ότι αν βρούμε μια θέση κάνουμε έξοδο από την For με την Exit (σε For Next θα βάζαμε Exit For)

Ελέγχουμε λοιπόν στην επόμενη if μετά την δομή For αν το ii>=0. Αν είναι -1 τότε εγκαταλείπουμε,. πάμε για άλλο n (στο repeat υπάρχει το n++ ως άμεση εντολή), πάμε για άλλο προορισμό. Δηλαδή βρήκαμε κενή θέση αλλά είδαμε ότι αυτή δεν έχει άλλη κενή θέση, και αφού δεν είναι η προτελευταία δεν μας κάνει. Αν πάμε εκεί δεν θα μπορούμε να πάμε αλλού, άρα δεν θα φτάσουμε στην final (64). Αυτό που κάνουμε λέγεται Look Ahead, κοίταγμα μπροστά, μια μόνο κίνηση.

Αν όντως υπάρχει δυνατότητα να πάμε αλλού τότε αυξάνουμε την Moves κατά ένα, βάζουμε στο (w,h) το νούμερο της Moves, βάζουμε στο σωρό BackTrack το τρέχον n και s με την Push, άρα το τελευταίο που μπήκε θα βγει πρώτο (LIFO), και αλλάζουμε δείκτη στην s δίνοντας την s1 και στην n δίνοντας ii (το οποίο n θα αλλάξει με το n++ στην Repeat, το ξέρουμε γιατί το Moves<last όταν μπήκαμε στο if και το πολύ να γίνει last, αλλά δεν θα γίνει final αφού last=final-1)


s1=Board(w, h) :ii=-1
for i=1 to len(s1){m1=stackitem(s1, i) :if Moves(m1#val(0),m1#val(1))>moves then ii=i-1 : exit
}
if ii>=0 then
    Moves++
    Moves(w,h)=Moves
    Stack BackTrack {Push n, s}
    s=s1: n=ii
end if

Και τώρα πάμε στην περίπτωση που μετά το n++ έχουμε στο While τη συνθήκη n>Len(s) αληθής.
Για ασφάλεια αν η BackTrack δεν έχει στοιχεία, δηλαδή έχει μήκος μηδέν θα γράψουμε ένα Break και θα εκτελέσουμε την Break που σπάει όλες τις φωλιασμένες επαναλήψεις μέχρι το τμήμα ή συνάρτηση που εκτελέστηκε. Η συνάρτηση θα γυρίσει κενό αλφαριθμητικό (εκτός αν είχαμε δώσει κάποια τιμή, στη Μ2000 η επιστροφή τιμής δεν κάνει τερματισμό κλήσης της συνάρτησης, μπορούμε να εκτελέσουμε και άλλες εντολές).

Μετά τον έλεγχο ασφαλείας, μειώνουμε την Moves, και ανοίγουμε το BackTrack και σηκώνουμε τα s και n. Σηκώνουμε (pop) σημαίνει ότι τα βγάζουμε από το σωρό. (η StackItem() διαβάζει τα στοιχεία χωρίς να τα βγάλει από το σωρό). Ο σωρός μας έχει  μια σειρά δυο διαφορετικών στοιχείων, το πρώτο ένας σωρός και το δεύτερο το n. Στην πράξη ο κάθε σωρός επειδή είναι δείκτης υπάρχει μια φορά. Ο σωρός BackTrack κρατάει τους δείκτες των σωρών όχι αντίγραφα αυτών. Επειδή δεν τους πειράζουμε τους σωρούς, δηλαδή να γράψουμε δεν έχουμε ζήτημα ακεραιότητας δεδομένων. Μόνο το BackTrack πειράζουμε, βάζοντας και βγάζοντας στοιχεία. Από τα s και n, διαβάζουμε το m, το πίνακα με τιμές για την θέση που θέλουμε να καθαρίσουμε, να πάρουμε πίσω δηλαδή, και βάζουμε την τιμή HighValue (to 100), στη Moves(). Στο repeat until, o πίνακας Board(), oi σωροί ως τιμές του, και οι πίνακες ως τιμές σε κάθε σωρό δεν αλλάζουν, απλά διαβάζονται. Αυτό που αλλάζει είναι ο BackTrack σωρός και ο πίνακας Moves() όπου θέλουμε να έχει το αποτέλεσμα. Τέλος αυξάνουμε το n κατά ένα,  γιατί ήδη το n έχει εξεταστεί. Αν το n είναι μεγαλύτερο από το μήκος του νέου s, τότε συνεχίζουμε να πετάμε από το BackTrack θέσεις. Αλλιώς πάμε για έξοδο από τη While (η While γράφεται και με While / End While χωρίς μπλοκ)

While n>len(s) {
    if Len(BackTrack)=0 then Print "Break", moves : Break
    Moves--
    Stack BackTrack {Read s, n}
    m=stackitem(s, n)
    Moves(m#val(0), m#val(1))=HighValue
    n++
}

Έτσι στην έξοδο από το While έχουμε το επόμενο n για μια θέση στο τρέχον s που δεν έχει εξεταστεί.

Κάποια στιγμή τελειώνει το repeat until και πάμε στη διαδικασία να φτιάξουμε τη διαδρομή. Επειδή έχουμε κάνει για λόγους βελτιστοποίησης μεταφορά της αρχικής θέσης στο τεταρτημόριο κάτω δεξιά, (δουλεύει με τα λιγότερα βήματα), και έχουμε κρατήσει σε δυο boolean μεταβλητές την αλλαγή, κάνουμε την αντίστροφη ενέργεια για να καθρεφτίσουμε το πίνακα Moves(). Ουσιαστικά αντί να καθρεφτίσουμε αυτόν αλλάζουμε τους επικεφαλίδες των αξόνων, και έτσι έχουμε το ίδιο αποτέλεσμα.

Το τελικό πρόγραμμα:


Function KnightTour$(StartW=1, StartH=1){
    def boolean swapH, swapV=True
    if startW<=4 then swapH=true: StartW=8+1-StartW
    if startH>4 then swapV=False: StartH=8+1-StartH
    Let final=8*8, last=final-1, HighValue=final+1
    Dim Board(1 to 8, 1 to 8), Moves(1 to 8, 1 to 8)=HighValue
    f=stack:=1,2,3,4,5,6,7,8
    if 8-StartW=2 and StartH=2 then stack f {shift 1,-8}
    Function KnightMove(x,w,h) {
        a=2:b=1:z=1:p=1
        if x mod 2=1 then swap a,b
        if x>2 then p-! : if x>4 then swap z, p : if x>6 then p-!
        w+=z*a
        h+=p*b
        if w>=1 and w<=8 and h>=1 and h<=8 then =(w, h) else =(,)
    }
    For j=1 to 8 :For i=1 to 8
    s=stack
    For k=1 to 8
        m=KnightMove(stackitem(f, k),i, j)
        if len(m)>1 then Stack s {data m}
    Next : Board(i,j)=s : Next
           stack f {shift 1,-8}
    Next
    For i=1 to 8 :For j=1 to 8
    s=Board(i, j)
    if len(s)>2 then
        so=queue
        For k=1 to len(s)
            m=stackitem(s, k)
            Append so, Len(Board(m#val(0), m#val(1))) :=m
        Next
        sort ascending so as number
        s=stack
        stack s {for k=0 to len(so)-1:data so(k!):next}
        Board(i,j)=s
    end if
    Next : Next
    s= Board(StartW, StartH)
    n=0
    BackTrack=Stack
    Moves=1
    Moves(StartW, StartH)=1
    Repeat
    n++
    While n>len(s) {
        if Len(BackTrack)=0 then Print "Break", moves : Break
        Moves--
        Stack BackTrack {Read s, n}
        m=stackitem(s, n)
        Moves(m#val(0), m#val(1))=HighValue
        n++
    }
    m=stackitem(s, n)
    w=m#val(0)
    h=m#val(1)
    if Moves(w, h)>=Moves then
        if Moves<last then
        s1=Board(w, h) :ii=-1
        for i=1 to len(s1){m1=stackitem(s1, i) :if Moves(m1#val(0),m1#val(1))>moves then ii=i-1 : exit
        }
        if ii>=0 then
            Moves++
            Moves(w,h)=Moves
            Stack BackTrack {Push n, s}
            s=s1: n=ii
        end if
        else
            Moves++
            Moves(w,h)=Moves
        end if
    end if
    until Moves=final
    Document export$
    Inventory Tour
    letters=stack:="a","b","c","d","e","f","g","h"
    f=stack:=1,2,3,4,5,6,7,8
    if swapV Else stack f {Shift 1,-8}
    if swapH then stack letters {Shift 1,-8}
    For j=1 to 8:For i=1 to 8
        Append Tour, Moves(i,j) :=stackitem$(letters, i)+str$(stackitem(f, j),"")
    Next : Next
    Sort ascending Tour as number
    one=each(Tour)
    While one {
        export$=Eval$(one)
        if not one^=last then export$="->"
        If (one^+1) mod 8=0 then
        export$={
        }
        End if
    }
    =export$
}
Document ex$
ex$= {Knight's Tour from a1
}+ KnightTour$()+{Knight's Tour from h1
}+ KnightTour$(8,1)+{Knight's Tour from a8
}+KnightTour$(1, 8)+{Knight's Tour from h8
}+KnightTour$(8, 8)
Clipboard ex$
Report ex$


Και παρακάτω το πρόγραμμα πειραματισμού. Μοιάζει με το πάνω, αλλά εδώ με ενδιέφερε να βγάλω για κάθε αρχική θέση τον αριθμό κινήσεων (ακόμα και αυτών που πήρε πίσω). Εδώ αντί για συνάρτηση έχω τμήμα Experiment. Με αυτό έκανα έλεγχο όλες τις θέσεις Δείτε ότι περνάω στοιχείο πίνακα με αναφορά (γίνεται σε κλήσεις τμημάτων και κανονικών συναρτήσεων και συναρτήσεων λάμδα, αλλά όχι ρουτινών και απλών συναρτήσεων - αυτές που φτιάχνονται με το Συνάρτηση Τέλος Συνάρτησης, είναι οι απλές συναρτήσεις). Το στοιχείο γράφεται στην mmm, και στο τέλος κλήσης, αντιγράφεται στο πίνακα (είναι μια λειτουργία copy in copy out). Με αυτό τον τρόπο δεν κλειδώνει ο πίνακας, και αν αλλάξουμε μέγεθος, τότε η επιστροφή τιμής στο πίνακα δεν βγάζει λάθος απλά χάνεται η τιμή. Τέτοιο σύστημα copy in copy out, έχουν και οι στατικές μεταβλητές. Τα στοιχεία πίνακα και οι στατικές δεν ανήκουν στο λίστα μεταβλητών για να παραστούν με αναφορά όπως για παράδειγμα μπορούμε να περάσουμε ολόκληρο πίνακα.με αναφορά. Η Μ2000 περνάει πίνακες και σαν τιμές, αλλά  οι τυχόν δείκτες σε τιμές του θα αντιγραφούν σαν δείκτες. Μόνο τα αντικείμενα Ομάδα, σε πίνακα, αντιγράφονται αν δεν έχουν φτιαχτεί δείκτες σε αυτές τις ομάδες. Έτσι η αντιγραφή πίνακα θεωρείται ρηχή αντιγραφή. Αντίγραφο σωρού βγάζουμε με την Σωρός(), έτσι η Stack(s) βγάζει ένα νέο σωρό με ρηχή αντιγραφή με ότι΄έχει ο s. Ομοίως για tuple έχουμε το Cons(), με μια παράμετρο, ενώ με πολλές ενώνει πολλούς πίνακες σε έναν νέο. Οι καταστάσεις δεν έχουν εντολές για πρόσθεση.Πρέπει να διατρέξουμε μία και να προσθέσουμε στοιχεία που διαβάζουμε σε άλλη.


module Experiment(StartW=1, StartH=1, &mmm){
    Global ww=8, hh=8
    def boolean swapH, swapV=True
    if startW<=ww div 2 then swapH=true: StartW=ww+1-StartW
    if startH>hh div 2 then swapV=False: StartH=hh+1-StartH
    Form 80, 32
    Let final=ww*hh, last=final-1, HighValue=final+1
    Dim Board(1 to ww, 1 to hh), Moves(1 to ww, 1 to hh)=HighValue
    f=stack:=1,2,3,4,5,6,7,8
    if ww-StartW=2 and StartH=2 then stack f {shift 1,-8}
    Function KnightMove(x,w,h) {
        a=2:b=1:z=1:p=1
        if x mod 2=1 then swap a,b
        if x>2 then p-! : if x>4 then swap z, p : if x>6 then p-!
        w+=z*a
        h+=p*b
        if w>=1 and w<=ww and h>=1 and h<=hh then
            =(w, h)
        else
            =(,)
        end if
    }
    For j=1 to hh
    For i=1 to ww
    s=stack
    For k=1 to 8
        m=KnightMove(stackitem(f, k),i, j)
        if len(m)>1 then Stack s {data m}
    Next
    Board(i,j)=s
    Next
           stack f {shift 1,-8}
    Next
    For i=1 to ww
    For j=1 to hh
    s=Board(i, j)
    if len(s)>2 then
    so=queue
    For k=1 to len(s)
    m=stackitem(s, k)
    Append so, Len(Board(m#val(0), m#val(1))) :=m
    Next
    sort ascending so as number
    s=stack
    stack s {
        for k=0 to len(so)-1
            data so(k!)
        next
    }
    Board(i,j)=s
    end if
    Next
    Next
    Print "Done"
    s= Board(StartW, StartH)
    n=0
    BackTrack=Stack
    Moves=1
    Moves(StartW, StartH)=1
    mstep=10
    mm=mstep
    mmm=0
    Repeat
    n++
    While n>len(s) {
        if Len(BackTrack)=0 then Print "Break", moves : Break
        Moves--
        Stack BackTrack {Read s, n}
        m=stackitem(s, n)
        Moves(m#val(0), m#val(1))=HighValue
        n++
    }
    m=stackitem(s, n)
    w=m#val(0)
    h=m#val(1)
    if Moves(w, h)>=Moves then
        if Moves<last then
        s1=Board(w, h) :ii=-1
        for i=1 to len(s1){m1=stackitem(s1, i) :if Moves(m1#val(0),m1#val(1))>moves then ii=i-1 : exit
        }
        if ii>=0 then
            Moves++
            Moves(w,h)=Moves
            Stack BackTrack {Push n, s}
            s=s1: n=ii
            mm--
        end if
        else
            Moves++
            Moves(w,h)=Moves
            mmm+=mm
            mm=0
        end if
    end if
    if mm Else
        cls
        Print "Moves:";Moves
        sj=hh: sj1=1: if swapV then swap sj, sj1
        si=1: si1=ww: if swapH then swap si, si1
        For j=sj to sj1: Print :For i=si to si1
            Print If(Moves(i,j)<=Moves->Moves(i,j), 0),
        Next : Next
        Print
        mm=mstep
        mmm+=mm
        Print mmm
        refresh 10000
    end if
    if mmm>1000 then mmm=-1: exit
    until Moves=final
        cls
        Print "Moves:";Moves
        sj=hh: sj1=1: if swapV then swap sj, sj1
        si=1: si1=ww: if swapH then swap si, si1
        For j=sj to sj1: Print :For i=si to si1
            Print If(Moves(i,j)<=Moves->Moves(i,j), 0),
        Next : Next
        Print
        Print mmm
        Print "DONE"
        refresh 5000
}
dim res(1 to 8, 1 to 8)=0
for j=8 to 1
      for i=1 to 8
            Experiment i, j, &res(i, j)
      next
next
Print
for j=8 to 1
      for i=1 to 8
            Print res(i, j),
      next
      Print
next