E-Scribe : a programmer’s blog

About Me

PBX I'm Paul Bissex. I build web applications using open source software, especially Django. Started my career doing graphic design for newspapers and magazines in the '90s. Then wrote tech commentary and reviews for Wired, Salon, Chicago Tribune, and others you never heard of. Then I built operations software at a photography school. Then I helped big media serve 40 million pages a day. Then I worked on a translation services API doing millions of dollars of business. Now I'm building the core platform of a global startup accelerator. Feel free to email me.

Book

I co-wrote "Python Web Development with Django". It was the first book to cover the long-awaited Django 1.0. Published by Addison-Wesley and still in print!

Colophon

Built using Django, served with gunicorn and nginx. The database is SQLite. Hosted on a FreeBSD VPS at Johncompanies.com. Comment-spam protection by Akismet.

Pile o'Tags

Stuff I Use

bitbucket, Django, Emacs, FreeBSD, Git, jQuery, LaunchBar, Markdown, Mercurial, OS X, Python, Review Board, S3, SQLite, Sublime Text, Ubuntu Linux

Spam Report

At least 236525 pieces of comment spam killed since 2008, mostly via Akismet.

Let's play a game: BASIC vs. Ruby vs. Python vs. PHP

In November I wrote about rediscovering BASIC Computer Games, a book I had when I was learning programming in the '80s. Flipping through it recently I came across a simple game called "Reverse":

The game of REVERSE requires you to arrange a list of numbers in numerical order from left to right. To move, you tell the computer how many numbers (counting from the left) to reverse. For example, if the current list is 2 3 4 5 1 6 7 8 9 and you reverse 4, the result will be 5 4 3 2 1 6 7 8 9. Now if you reverse 5, you win.

The original BASIC version, including instructions and comments, is about 60 lines. It seemed like a good candidate for a quick language comparison; a chance to learn a little more Ruby and to see how it felt compared to two languages I'm more familiar with, Python and PHP. (If I had any skill in Perl or Tcl or Scheme they'd be included as well. Javascript should probably be here too.)

Update: Not knowing when to stop, I've since done versions in Logo and Io and Lua and Scheme. Readers have contributed versions in F-Script, False, Haskell, JavaScript, Lisp, Perl, Prolog, REBOL, Squeak, Tcl, and VBScript.
Another update: A scan of the original page is viewable at the Atari Archives.

I also wanted to see just how much shorter these new versions would be -- the BASIC program takes about eight lines just to initialize the array of shuffled numbers.

The three programs are reproduced below; all were tested on my PowerBook running OS X 10.4.3, with Ruby 1.8.2, Python 2.4.1, and the PHP CLI 4.3.11. Their output looks identical in the shell. Here's the end of a game:

...
3 1 2 4 5 6 7 8 9
Reverse how many? 3
2 1 3 4 5 6 7 8 9
Reverse how many? 2
Done! That took you 9 steps.

Here's the code. I tried to be fair with the line and byte counts. I do think the counts represent the languages' respective verbosity levels in a general way.

Ruby (10 lines, 274 bytes)

numbers = (1..9).sort_by{ rand }
steps = 0

while numbers != numbers.sort
  puts numbers.join(" ")
  print "Reverse how many? "
  flipcount = gets.to_i
  numbers[0...flipcount] = numbers[0...flipcount].reverse
  steps += 1
end

print "Done! That took you #{steps} steps.\n"

Python (10 lines, 324 bytes 9 lines, 304 bytes)

Updated: see comments (original version)
import random

numbers = random.sample(range(1,10), 9)
steps = 0

while numbers != sorted(numbers):
    print " ".join(map(str, numbers))
    flipcount = int(raw_input("Reverse how many? "))
    numbers[:flipcount] = reversed(numbers[:flipcount])
    steps += 1

print "Done! That took you %d steps." % steps

PHP (12 lines, 381 bytes)

$numbers = range(1, 9);
shuffle($numbers);
$sorted = $numbers;
sort($sorted);
$steps = 0;

while ($numbers != $sorted)
    {
    print implode(" ", $numbers) . "\n";
    print "Reverse how many? ";
    $flipcount = (int)trim(fgets(STDIN));
    array_splice($numbers, 0, $flipcount, array_reverse(array_slice($numbers, 0, $flipcount)));
    $steps++;
    }

print "Done! That took you $steps steps.\n";

Observations

Saturday, January 14th, 2006
+ + + +
150 comments

Comment from metapundit , 2 days later

Hmm. I've been meaning to really learn python for a while now and have been resisting the ruby bandwagon. That ruby code sure is readable tho...

You can see how Python's strong typing adds a bit of verboseness with the explicit conversion to string in the join. I would've mapped tho (ie " ".join(map(str,numbers) seems more terse and more readable at the same time...)

Sadly, I write PHP full time for a living...

Comment from Erik Wilsher , 2 days later

Some thing I would do differently in the python sample:

Initialization of data: numbers = random.sample(range(10),10)

Reversing the flip: numbers[:flipcount] = reversed(numbers[:flipcount])

Comment from Paul , 2 days later

All good suggestions, thanks! I've updated the code to reflect them. I had actually played with reversed() but was tripped up while experimenting by the fact that r = reversed([1,2,3]) gets the iterator object; you need to assign to a slice (e.g. r[:] = reversed([1,2,3])) to get a reversed list.

Comment from Palo , 2 days later

here is a way how to increase the number of lines in the python code:

numbers=range(1,10) random.shuffle(numbers)

(I find it a bit more readable than random.sample(range(1,10), 9))

Also, in python2.4 you can use

" ".join(str(n) for n in numbers)

in place of

" ".join([str(n) for n in numbers])

(saved 2 characters!). [to save even more characters, one could use

flipcount = input("Reverse how many? ")

but I think that's justly considered dangerous]

Comment from Dominic Fox , 2 days later

My Haskell version is here:

http://codepoetics.com/poetix/index.php?p=213

There's a certain amount of scaffolding that we have to put in ourselves, but the main loop's tolerably concise.

Comment from masklinn , 2 days later

Metapundit > Ruby's type system is no weaker than Python's, both are strongly dynamically typed languages.

The difference is that Ruby's Array#join method calls to_s (string conversion) on every element of the array (which is explicitely stated in the documentation, check ri Array#join) while Python's str.join doesn't (here again, it's explicitely stated in the documentation that join takes an array of strings).

Comment from Filip , 2 days later

In the python version, you could do random.sample("123456789", 9) to get away with the cleaner " ".join(numbers) later on.

The initialization in the PHP version also looks a bit bulky, and could probably be cut down to something like:

$sorted = $numbers = range(1, 9);
shuffle($numbers);
$steps = 0;
Comment from Andrew Dalke , 2 days later

More traditionally the way to reverse list elements in Python is

numbers[:flipcount] = numbers[flipcount-1::-1]

for example

>>> x=[1,2,3,4,5]
>>> i=2
>>> x[:i] = x[i-1::-1]
>>> x
[2, 1, 3, 4, 5]
>>> i=4
>>> x[:i] = x[i-1::-1]
>>> x
[4, 3, 1, 2, 5]
>>>
Comment from Paul , 2 days later

In Python, save some bytes with

print " ".join(map(str, numbers))

Bye ...

Comment from Pat Maupin , 2 days later

Python version could be somewhat shorter.

numbers[:flipcount] = numbers[flipcount-1::-1]

I find this actually clearer than reversed().

Comment from metapundit , 2 days later

masklinn:

Ruby’s type system is no weaker than Python’s, both are strongly dynamically typed languages.

The difference is that Ruby’s Array#join method calls to_s (string conversion) on every element of the array (which is explicitely stated in the documentation, check ri Array#join) while Python’s str.join doesn’t (here again, it’s explicitely stated in the documentation that join takes an array of strings).

My mistake. I was thinking ruby was weakly dynamically typed (like PHP is) and python was strongly dynamically typed... Aside from the typing issues, does seem like python's join should implicitly convert list elements to strings since join is a string only operation... Ruby definitely does the right thing here and I guess I have to chalk the terseness up to better library design rather than difference in typing...

Comment from metapundit , 2 days later

Nobody buys the superiority of map over list comprehensions? I just see

print " ".join([str(n) for n in numbers])

as a little verbose to mentally unpack. I can't help seeing for as looping construct so working from inmost construct I see "okay, loop over list, converting current item to string, making a new list of the result", and join by a space.

" ".join(map(str,numbers))

I look at and think "map array to array of strings", and join by a space.

Comment from Paul , 2 days later

I buy it, and in fact I changed the code accordingly! (Somehow it didn't make it in with the changes I made last night.)

Comment from Christophe , 2 days later

I'll have to say that the Ruby version isn't a good idom. sort_by{ rand } isn't a true random shuffle as done in Python.

http://eigenclass.org/hiki.rb?sort_by+rand+is+biased

Ruby loses points for an invalid program :) And on the point of ease of reading, the version : numbers = range(1,10) random.shuffle(numbers)

is the best in my opinion. Not everything needs to be a one liner.

Comment from Krys , 2 days later

In the Python code you can save 2 more lines at the expense of readability. Python can so several assignments on the same line so you could have

numbers, steps = random.sample(range(1,10), 9), 0

and

numbers[:flipcount], steps = reversed(numbers[:flipcount]), steps + 1

I don't know anyone who would do that in "real" code, however. :-)

Comment from Filip , 2 days later

From another angle, all three programs would benefit from being a few lines longer. Based on pure principles, if nothing else.

Comment from anonymous , 2 days later

raw_input returns strings, input() returns whatever python thinks is appropriate; i.e. converting to int implicitly.

flipcount = input("Reverse how many? ")

Gets python below 300bytes.

Comment from Paul , 2 days later

Filip, I agree with you, at least on the Python and PHP -- when working on short little programs like this it's easy to get carried away with condensing things.

I'm not sure that I'd get much out of breaking down the Ruby version further, but I suppose the real test would be to see what I think of it in six months.

Comment from Sebastian , 3 days later

A variation of the Python one, more easy to follow, but a bit more verbose. And the reverse, while easy to guess what it does, not so easy to see why.

import random
reverse = -1

numbers = random.sample(range(1,9+1), 9)
steps = 0

while numbers != sorted(numbers):
    print " ".join(map(str, numbers))
    flipcount = input("Reverse how many? ")
    numbers[:flipcount] = numbers[flipcount-1::reverse]
    steps += 1

print "Done! That took you %d steps." % steps
Comment from Sebastian , 3 days later

Oops, the first two lines went into the paragraph there. Maybe there should be a preview function, if not even an edit function?

Comment from Paul , 3 days later

Fixed it for you -- but yes, preview is on my to-do list...

Comment from Asokan Pichai , 4 days later

Again some more explicitness - adding to length seems to me to be a nice thing here

import random
base = range( 1, 10 )

numbers = random.sample( base, 9 )
steps = 0

while numbers != base:

I also like the avoiding of sorting to produce a constant list.

Comment from Terry , 5 days later

The Python version could get its byte count down - and be cleaner at the same time - by using flipcount = input("Reverse how many? ")

instead of

flipcount = int(raw_input("Reverse how many? "))

Comment from Pascal , 4 weeks later

Funny game, here is a REBOL version of this game. An interesting thing is its readability.

numbers: random ordered: [1 2 3 4 5 6 7 8 9]
count: 0

while [numbers  ordered][
    reverse/part probe numbers to-integer ask "Reverse how many? "
    count: count + 1
]

print reform ["Done! That took you" count "steps."]
Comment from Pascal , 4 weeks later

Markdown ate the "different" operator in the "while" statement, which is a less-than followed by a greater-than

Try again: while [numbers < > ordered][

Or you can also use this: while [not-equal? numbers ordered][

Cheers

Comment from Paul , 4 weeks later

I love the REBOL version, thanks!

(The dropped angle brackets were due to some crude HTML-stripping I do for anti-spam and anti-XSS purposes, actually -- not Markdown's fault. I need to either clean it up or clarify my instructions.)

Comment from Arun , 5 weeks later

Hi,

Thanks for inspiring me to write a javascript version of REVERSE. You can play it online at:

http://www.arunrocks.com/blog/archives/2006/02/15/reverse-a-javascript-game-in-24-hours/

Comment from Paul , 5 weeks later

That's excellent!

Comment from quag , 5 weeks later

Here is a slightly tweaked Io version.

numbers := list(1,2,3,4,5,6,7,8,9) shuffle
count := 0
while (numbers != numbers clone sort,
    count = count + 1
    writeln(numbers join(" "))
    "How many to swap? " print
    howMany := File standardInput readLine asNumber
    numbers = numbers slice(0, howMany - 1) reverse appendSeq(numbers slice(howMany))
)
writeln("Done! That took you ", count, " steps.")
Comment from Paul , 5 weeks later

Cool, thanks. (I took the liberty of combining your two comments, hope that's OK.)

I like the anonymous object trick there: "numbers clone sort".

I considered the one-line swap but found it a bit harder to read. Is it generally more, er, "Ionic" to leave long message chains unbroken?

Comment from Olivier Ansaldi , 5 weeks later

Paul, here's a LISP version. I'm a bit rusty on the ol' LISP so I'm pretty sure someone out there will find a short/cleaner version.

(defun shuffle (l)
  (loop for i below (length l) do (rotatef (elt l i) (elt l (random (length l)))))
  l)

(defun prompt (l)
  (format t "~{~a~^, ~}~%How many to swap? " l)
  (read))

(defun playgame (n)
  (let ((goal (loop for i from 1 to n collect i)))
    (do ((count 0 (1+ count))
     (state (shuffle (copy-list goal))
            (let ((howmany (prompt state)))
              (append (reverse (subseq state 0 howmany)) (subseq state howmany)))))
        ((equal state goal) (format t "Done! that took ~d steps.~%" count)))))
Comment from Olivier Ansaldi , 5 weeks later

Here's my Prolog implementation. Check out: http://ozone.wordpress.com/2006/02/22/little-prolog-challenge/

Prolog "standard library" lacks the depth of languages like Ruby so a good part of the code is there to implement support predicates (shuffle, split). Prolog really shines for the game loop.

Comment from Paul , 5 weeks later

Olivier, thanks for those. I'm envious that you got to Prolog before I did!

Compared to my hacky Scheme version (link posted in update notice up top), your Lisp looks quite compact.

Comment from tim bates , 6 weeks later

php in 9 lines...

$sorted  = $numbers = range(1, 9);
shuffle($numbers);
$steps = 0;
while ($numbers != $sorted) {
    print implode(" ", $numbers) . "\n Reverse how many? ";
    $flipcount = (int)trim(fgets(STDIN));
    array_splice($numbers, 0, $flipcount, array_reverse(array_slice($numbers, 0, $flipcount)));
    $steps++;
}
print "Done! That took you $steps steps.\n";
Comment from Philippe Mougin , 10 weeks later

Here is an implementation in F-Script:

http://www.fscript.org/reverseGame.htm

This page also shows the implementation of a program that plays to the game.

Comment from Paul , 10 weeks later

Beautiful, Philippe, thanks!

Comment from Nick , 10 weeks later

As awesome as that DHTML version is, here's a simpler javascript that fits the original format (type 'cscript whateverYouNamedIt.js' to run):

// inordinate amounts of scaffolding

Array.prototype.dupe = function() { return this.slice(0); } // hacky
Array.prototype.empty = function() { while (this.length > 0) this.pop(); return this; }

Array.prototype.equals = function(that) {
    if (this.length != that.length) return false;
    for (var i = 0; i < this.length; ++i) {
        if (this[i] != that[i]) return false;
    }
    return true;
}

Array.prototype.appendAry = function(ary) {
    var result = this.dupe();
    for (var i = 0; i < ary.length; ++i) {
        result.push(ary[i]);
    }
    return result;
}

Array.prototype.shuffle = function() {
    var source = this.dupe();
    this.empty();
    while (source.length > 0) {
        var idx = Math.floor(Math.random() * source.length);
        this.push(source.splice(idx, 1));
    }
    return this;
}

Array.prototype.reverseLeft = function(count) {
    return this.slice(0, count).reverse().appendAry(this.slice(count));
}

// main code

var solved = new Array(1,2,3,4,5,6,7,8,9);
var numbers = solved.dupe().shuffle();
var steps = 0;

while (!numbers.equals(solved)) {
    WScript.StdOut.Write(numbers.join(" ") + "\nHow many numbers to swap? ");
    var howMany = +(WScript.StdIn.ReadLine());
    numbers = numbers.reverseLeft(howMany);
    steps++;
}

WScript.Echo("Done! That took you " + steps + " steps!");
Comment from Nick , 10 weeks later

Meh. I decided it was a pinch too long, so I nixed appendAry() and reimplemented reverseLeft() like so:

Array.prototype.reverseLeft = function(count) {
    var left = this.slice(0, count).reverse();
    var right = this.slice(count);
    while (right.length > 0) left.push(right.shift());
    return left;
}
Comment from Nick , 11 weeks later

Triple post! I had some free time this weekend, so I decided to torture myself, er, that is, implement a false version. False doesn't provide a randomization faciility, so the ordering is hard-coded. It was the best I could do. You may now officially ph34r me:

0 3 1 4 2 5 9 7 8 6
0[$1+[$O@1-$][@]#%\%]d:s:
[10d;!1_1[@$][\$@=@&\1+]#%%~]
[9d;!.9[1-$][" "\.]#%
 10,"How many to swap? "B^^B%'0-$1>
 [$2>~[%\0]?$3=[%\@0]?$3>
  [$c:[$1>][\10*\1-@@+\]#
   c;[$1>][\10*\1-]#%[$1>]
   [$@$@/@@\$@$@/@$@*@\-\10/]#
  ]?1s;+s:]?%]#[][]#s;
"Done! That took you "." steps!"

You gotta admit. It looks gorgeous. Oh, and I second comment previews.

Comment from Paul , 11 weeks later

Wow.

Comment from Eric , 14 weeks later

Here's a perl version.

@array = (1..9); $steps = 0; shuffle( \@array ); while (@array != sort {$a $b} @array){print join(" ", @array) . "\nReverse how many?\n"; $num = ; splice(@array, 0, $num, reverse(@array[0..($num-1)])); $steps++; } print "Done! That took you $steps steps.\n"; sub shuffle {my $array = shift; my $i; for ($i = @$array; --$i; ) {my $j = int rand ($i+1); next if $i == $j; @$array[$i,$j] = @$array[$j,$i];}}

Comment from Eric , 14 weeks later

Sorry I was being lazy, now it's even better (just take everything between the lines):


@y=(1..9);f(\@y);while(@y!=sort{$a$b}@y){print join(" ",@y)."\nReverse how many?\n";$m=;splice(@y,0,$m,reverse(@y[0..($m-1)]));$n++;};print "Done! That took you $n steps.\n";sub f{my $y=shift;for($i=@$y;--$i;){$j=int rand($i+1);next if$i==$j;@$y[$i,$j]=@$y[$j,$i];}}


Comment from Eric , 14 weeks later

BTW- that's all one line, so that means perl wins!

Comment from Eric , 14 weeks later

Dangit!

I didn't actually test it, the last two had a flawed loop condition, this one actually works (tested it), still one line though, (would be smaller but I had to created a decent shuffle function).


@y=(1..9);f(\@y);while((join('',@y))!=(join('',sort{$a$b}@y))){print join(" ",@y)."\nReverse how many?\n";$m=;splice(@y,0,$m,reverse(@y[0..($m-1)]));$n++;};print "Done! That took you $n steps.\n";sub f{my $y=shift;for($i=@$y;--$i;){$j=int rand($i+1);next if$i==$j;@$y[$i,$j]=@$y[$j,$i];}}


Comment from Eric , 14 weeks later

Just keeps getting better, now it will tell you how long it took you as well as how many steps!


@y=(1..9);f(\@y);while((join('',@y))!=(join('',sort{$a$b}@y))){print join(" ",@y)."\nReverse how many?\n";$m=;splice(@y,0,$m,reverse(@y[0..($m-1)]));$n++;};print "Done! That took you $n steps and ".(time-$^T)." seconds.\n";sub f{my $y=shift;for($i=@$y;--$i;){$j=int rand($i+1);next if$i==$j;@$y[$i,$j]=@$y[$j,$i];}}


Comment from Paul , 14 weeks later

Thanks for the contribution. It's simplicity itself!

Comment from Eric , 14 weeks later

Here's another one. This one is for vbscript. Just put this into a file and give it an extension of .vbs (on a windows machine of course) and it should work on most new windows machines (uses wscript host).

I had a heck of a time with the shuffle on this one, I don't know vbscript that well.


Dim wshShell
Dim s
Dim vtt
Dim vst
Dim vs
Dim myArray(8)
Dim sarray

Set wshShell = Wscript.Createobject("Wscript.shell")
If InStr(Ucase(wscript.FullName), "CSCRIPT") = 0 Then
 wshShell.Run "cscript.exe //nologo " & Chr(34) & WScript.ScriptFullName & Chr(34)
 Wscript.Quit
End If

do until (s = "q")
 wscript.stdout.writeline "Press Enter to play!"
 wscript.stdout.write "(Type 'q' at any time to quit the game)"
 s = wscript.stdin.readline
 if (s = "q") then
  wscript.quit
 end if

 vs = time
 vst = 0

 for a=0 to 8
  myArray(a)=(a+1)
 next

 randomize
 do until (myarray(0)  1 and myarray(1)  2 and myarray(2)  3 and myarray(3)  4 and myarray(4)  5 and myarray(5)  6 and myarray(6)  7 and myarray(7)  8 and myarray(8)  9)
  for b=8 to 0 step -1
   randomize
   c = Int((b+1)*Rnd)
   if bc then
    d=myarray(c)
    myarray(c)=myarray(b)
    myarray(b)=d
   end if
  next
 loop

 do until (myarray(0) = 1 and myarray(1) = 2 and myarray(2) = 3 and myarray(3) = 4 and myarray(4) = 5 and myarray(5) = 6 and myarray(6) = 7 and myarray(7) = 8 and myarray(8) = 9)
  vst = vst + 1
  for each e in myarray
   wscript.stdout.write e & " "
  next

  wscript.stdout.writeline
  wscript.stdout.writeline "Reverse how many?"
  f = wscript.stdin.readline

  if IsNumeric(f) = False then
   if (f = "q") then
    wscript.quit
   end if
   wscript.stdout.writeline "Pick a NUMBER!!!"
  elseif f = 1 then
   wscript.stdout.writeline "That's pointless, pick again!"
  elseif f < 1 then
   wscript.stdout.writeline "Ok, you know you can't do that!"
   wscript.stdout.writeline "Pick again you fool!"
  elseif f > 9 then
   wscript.stdout.writeline "Ok, stop messing around!"
  end if

  if (IsNumeric(f) = False) then
   f = 0
  end if

  if (f > 1 and f < 10) then
   redim sarray(f-1)
   for g = 0 to (f-1)
    sarray(g)=myArray((f-1)-g)
   next
   for g = 0 to (f-1)
    myArray(g)=sarray(g)
   next
  end if
 loop

 vtt = datediff("s",vs,time)

 wscript.stdout.writeline "Congratulations, you have won!"
 wscript.stdout.writeline "It only took you " & vst & " steps and " & vtt & " seconds!"
 wscript.stdout.writeline
loop

Comment from Avi , 6 months later

Here is Tcl version (tcllib needed)

package require math
package require struct::list

set input {1 2 3 4 5 6 7 8 9}
set base $input
set steps 0
for {set i 0} {$i < [llength $base]} {incr i} {
    set index [::math::random 0 [llength $input]]
    lappend myarray [lindex $input $index]
    set input [lreplace $input $index $index]
}
while {[::struct::list equal $myarray $base]==0} {
    incr steps
    puts -nonewline "$myarray \n($steps) How Many? "
    flush stdout
    set howmany [gets stdin]
    set myarray [concat [::struct::list reverse [lrange $myarray 0 [expr $howmany-1]]] [lrange $myarray $howmany end]]
}
puts "$myarray \nDone in $steps steps"
Comment from Paul , 6 months later

Markus Gaelli offers a Squeak (Smalltalk) version:

steps := 0.
numbers := (1 to: 9) asArray shuffled.
[numbers isSorted] whileFalse:
        [flipCount:= numbers indexOf: ((SelectionMenu selections: numbers)startUpWithCaption: \ 
            'Revert up to which number?' at: Display center  ).
        1 to: flipCount//2 do: [:i | numbers swap: i with: flipCount-i+1].
        steps := steps + 1].PopUpMenu inform: 'You needed ', steps asString,' \
            steps to sort the list.'

He's also created a more elaborate eToys version (screenshot) that you can run in your browser if you've got the Squeak plugin.

Comment from BlueSun , 7 months later

PHP in 7 lines:

$sorted  = $numbers = range(1, 9);
shuffle($numbers);

for ($steps = 0; $numbers != $sorted; $steps++) {
    print implode(" ", $numbers) . "\n Reverse how many? ";
    $flipcount = (int)trim(fgets(STDIN));
    array_splice($numbers, 0, $flipcount, array_reverse(array_slice($numbers, 0, $flipcount)));
}

print "Done! That took you $steps steps.\n";
Comment from Cannavaro , 7 months later

When you mentioned Forth, I took it for a challenge ^___^ I have seen the language just for a brief time back in school years, so the following code almost certainly sucks, but at least it works (under gForth).

I wrote in a simple PRNG (shamelessly lifted from the language manual) but you should update the seed variable at first if you don't want to play always the same game... For the "line count" part, line breaks and indentation are there for readability only; they don't matter in Forth.

Anyway, I just can't understand why that little neat piece of Ruby reminded you of this! o___O

=========================================================

hex
ff800000 constant ROL9MASK
decimal

variable seed

: ROL9 ( u1 -- u2 | rotate u1 left by 9 bits )
    dup ROL9MASK and 23 rshift swap 9 lshift or ;     
: RANDOM ( -- u ) seed @ 107465 * 234567 + rol9 dup seed ! ;
: RAND RANDOM abs swap mod ;

: NEWLINE 13 emit 10 emit ;

create workset 10 cells allot

: FILLNUMS
    workset
    10 0 DO
        dup i dup cells rot + !
    LOOP drop ;

: PRINTNUMS
    workset
    10 0 DO
        dup i cells + @ .
    LOOP NEWLINE drop ;

: MIXARRAY
    1 9 -DO
        workset i cells + dup @
        workset i RAND cells + dup @
        rot rot ! swap !
    1 -LOOP ;

: REVERSE
    dup 2 / 0
    ?DO
        1 - dup cells workset + dup @
        workset i cells + dup @
        rot rot ! swap !
    LOOP drop ;

: CHECK
    1
    9 0 DO
        workset i cells + dup cell +
        @ swap @ - 
        1  IF <> 1- unloop exit THEN
    LOOP ;

: MESSAGE s" How many numbers do you want to reverse? (press 0 for 10) " type
  key dup emit NEWLINE 48 -
  dup 0= IF drop 10 THEN dup 2 11 within invert IF drop 0 THEN ;

: PLAY 0 NEWLINE FILLNUMS MIXARRAY
  BEGIN PRINTNUMS MESSAGE REVERSE 1+ CHECK UNTIL
  s" You solved the game in " type . s"  steps" type ;
Comment from Cannavaro , 7 months later

Well, I wrote the "unequal" operator and had it stripped just like another reader before... the last line of "check" was similar to this

1 < > IF 1- unloop exit THEN

Comment from Paul , 7 months later

Fixed -- sorry about that! It's a vestigal anti-comment-spam measure. Akismet is working pretty well so I may change my anti-angle-bracket policy.

Comment from Thorsten , 7 months later

Finally, which language is the winner?

Comment from Paul , 7 months later

In the words of David Letterman: It's an exhibition, not a competition!

Comment from Jessica , 7 months later
$sorted = range(1, 9);
$numbers = $sorted;
shuffle($numbers);
$steps = 0;

You can cut a line out of the PHP code if I understand the range() function correctly. Why shuffle it and resort it?

Comment from Paul , 7 months later

No good reason that I can see now. I think that sequence is an artifact of an earlier version.

Comment from Bill , 8 months later

Here's a readable perl version (9 lines, 365 chars):

use FreezeThaw qw(cmpStr);
my @sorted  = (1 .. 9);
my @numbers = sort {rand(10) > $a} @sorted;
for (my $steps = 0; cmpStr(\@numbers, \@sorted) != 0; ++$steps) {
    print join(" ", @numbers), "\nReverse how many? ";
    my $flipcount = ;
    splice(@numbers,0,$flipcount,reverse( @numbers[0..($flipcount-1)]));
}
print "Done! That took you $steps steps.\n";
Comment from Olivier Mengué , 8 months later

More Perl 5 and Perl 6 versions (including Larry's) in the perl.perl6.users newgroup thread.

Comment from Mike , 8 months later

Anyone willing to post an assembler version? :)

Comment from Coaching , 9 months later

PHP looks really ugly, the python code seems to be the slimmest...

Comment from Olivier Mengué , 9 months later

Here is one version in the Microsoft Command Processor, better known as the "DOS prompt".

@echo off

verify other 2>nul
setlocal enabledelayedexpansion
if errorlevel 1 echo Windows 2000/XP required.& goto :EOF

set sorted=%2
set sorted=%sorted:"=%
if "%sorted%"=="" (
    set sorted=123456789
    rem set sorted=ABCDE
)


set current=%1
set current=%current:"=%
if "%current%"=="" (
    set current=%sorted%
    call :Shuffle current
)

set steps=0

:Play
echo.%current%
if "%current%"=="%sorted%" goto Done
set /P flipcount=Reverse how many? 
if /I "%flipcount:~0,1%"=="q" goto :EOF

set left=!current:~0,%flipcount%!
set right=!current:~%flipcount%!
call :Reverse left
set current=%left%%right%

set /A steps+=1
goto Play


:Done
:: The only way to print '!' is to disable DelayedExpansion.
:: Yes, cmd.exe is really flawed: there is no escape char for '!' in
:: DelayedExpansion mode.
setlocal disabledelayedexpansion
echo Done! That took you %steps% steps.
endlocal
goto :EOF


:: ======= FUNCTIONS =======

:: Compute number of characters of variable named %1
:: Result is returned in both %errorlevel% and %Length%
:Length
setlocal enabledelayedexpansion
set l=0
:LengthLoop
set _=!%1:~%l%,1!
if not "%_%"=="" set /A l+=1& goto LengthLoop
endlocal & set Length=%l%
:: Set errorlevel and return
exit /B %Length%


:: Reverse characters of variable named %1
:Reverse
setlocal enabledelayedexpansion
call :Length %1
set /A p=Length/2
set r=!%1:~%p%,-%p%!
:ReverseLoop
set /A q=p-1
set r=!%1:~-%p%,1!%r%!%1:~%q%,1!
set p=%q%
if not %p%==0 goto ReverseLoop
endlocal & set %1=%r%
goto :EOF


:: Shuffle characters in variable named %1
:Shuffle
setlocal enabledelayedexpansion
call :Length %1
set /A count=5*Length
set d=!%1!
for /L %%i in (1,1,%count%) do call :ShuffleSub d
endlocal & set %1=%d%
goto :EOF
:ShuffleSub
set /A n=^(Length * %RANDOM%^) / 32768
set right=!%1:~%n%!
set %1=!right:~1!!%1:~%n%,1!!%1:~0,%n%!
::echo.!%1!
goto :EOF
Comment from Paul , 9 months later

Thanks for that. Pretty funny to see, especially the workaround for the exclamation point!

Comment from cormullion , 10 months later

This is a very nice post and I enjoyed the good-natured comments. It's been interesting looking at all the different solutions.

Here's my newLISP version (is newLISP - diabolus in programmatica - allowed? ;-))

(define (rev-slice n lst)
     (append (reverse (0 n lst)) (n lst)))

(set 'steps 0 'goal (sequence 1 10) 'state (randomize goal))

(until (= goal state)
    (println state "\nReverse how many?")
    (inc 'steps)
    (set 'state (rev-slice (int (read-line)) state)))
(string "\\nDone! That took you " steps " steps!")

newLISP is concise but not too terse (if there's a difference). A bit less punctuation and fewer abbreviations (but plenty of parentheses, of course) make it high on bytes but low on lines.

Comment from cormullion , 10 months later

PS: i thought I had indented that code so that a Markdown pass would keep it formatted correctly, but I obviously goofed somewhere. I don't normally put every statement on a single line...! ;-) As originally written, there are 7 lines.

Comment from Paul , 10 months later

Neat, thanks -- I didn't know about newLISP.

I took the liberty of fixing the code formatting (Markdown wants four spaces at the beginning of a code line).

Comment from halabuda , 10 months later

In regards to the PHP version (and probably other languages as well) the initialization line: $steps=0; is unnecessary in terms of minimizing code because the increment operator will assume a value of 0 for undefined variables. The error suppression operator can be used to ignore any PHP notices about undefined variables. ie: @$steps++;

Comment from Paul , 10 months later

Spoken like a true codegolfer!

Comment from Nick , 10 months later

Hello again! I'm the nut-job responsible for that awful False version above. I decided to give Haskell a shot:

module Reverse where
import System.Random

prompt q = catch (putStr q >> readLn) $ const $ prompt q

play s [1,2,3,4,5,6,7,8,9] =
 putStrLn $ "Done! That took you " ++ show s ++ " steps!"
play s xs = do
 putStrLn $ unwords $ map show xs
 n <- prompt "Reverse how many? "
 play (s + 1) $ reverse (take n xs) ++ drop n xs

shuffle [] = return []
shuffle xs = do
 i <- randomRIO (0, length xs - 1)
 rest <- shuffle $ take i xs ++ drop (i + 1) xs
 return $ xs !! i : rest

main = play 0 =<< shuffle [1..9]

I like the shuffle function. It basically yanks a random element out of the list, sticks it in front, and shuffles the remainder recursively. I took a look at Dominic's version; his effectively pairs each element with a random number, sorts the list by that random number, and strips them off. I smooshed together his shuffle and shuffleWith functions, then yanked the stateful IO up into main:

module Reverse where
import System.Random
import Data.List

prompt q = catch (putStr q >> readLn) $ const $ prompt q

play s [1,2,3,4,5,6,7,8,9] =
 putStrLn $ "Done! That took you " ++ show s ++ " steps!"
play s xs = do
 putStrLn $ unwords $ map show xs
 n <- prompt "Reverse how many? "
 play (s + 1) $ reverse (take n xs) ++ drop n xs

shuffle xs gen = map snd $ sort $ zip (randoms gen :: [Int]) xs

main = play 0 . shuffle [1..9] . mkStdGen =<< randomIO

The only differences here are shuffle, main, and the addition of import Data.List (for sort).

Comment from Paul , 10 months later

Nice. I think Haskell's next for me.

(I fixed the "<" problem for you -- my tag-tripping code, an old antispam measure which I just removed, was getting in your way there.)

Comment from Nick , 10 months later

And I highly recommend it! It was one of the most painful, brain-breaking experiences I've had learning a new language, but I know I'm better for it. The type system alone is worth the headache. It's magic. Haskell.org has a great Getting Started guide. I used Hugs at first, and it's alright, but GHC's error messages are much friendlier. Unfortunately, it practically installs a Linux distro on my Windows box. *cries*

Work your way through Yet Another Haskell Tutorial until you have a loose grasp on the type system, before you try your hand at Monads. At some point, everyone seems to grumble about Monads in general and IO in particular. Writing Monad tutorials is a rite of passage for the Haskell newbie. The clearest that I've seen is You Could Have Invented Monads.

Dive in, and don't give up! Good luck!

(Oh, and you missed a few &lt;'s in my code there. The first snippet has 3 lines that start n &lt;-, i &lt;-, and rest &lt;-, the second snippet has a n &lt;-. Could you please fix 'em?)

Comment from Internetfirma Köln , 10 months later

(define (rev-slice n lst) (append (reverse (0 n lst)) (n lst)))

(set 'steps 0 'goal (sequence 1 10) 'state (randomize goal))

(until (= goal state) (println state "\nReverse how many?") (inc 'steps) (set 'state (rev-slice (int (read-line)) state))) (string "\nDone! That took you " steps " steps!")

This code don't work to me, something is wrong but i can't understand what :( Help me pls!

Comment from cormullion , 10 months later

Works fine for me. MacOS X 10.4.8, newLISP v 9.0.4.

Best to take this discussion to rather than here!

Comment from cormullion , 10 months later

:-) oops it swallowed the url (where's that preview? :-)

I meant 'take this discussion to http://www.alh.net/newlisp/phpbb/'

sorry Paul

Comment from Nick , 10 months later

Hee-hee. I think you'll like this, Paul.

Comment from Paul , 11 months later

What?

Why do I get so many inscrutable comments with German URLs?

Note: It may be hard to understand what I'm saying here since I've deleted dozens of spam comments in this thread. They're typically almost-relevant sounding short sentences like "thank you for sharing this script", with a username that doesn't match the name given in the comment body, and a URL pointing to a .de (German) domain -- never an internal page, always the top level. Weird.

Comment from cormullion , 11 months later

I wonder whether someone is paying them to get their URL on other people's web pages... I presume that's what's behind it. Perhaps its a German thing...?

Comment from Marc Hansen , 11 months later

PHP is the best for me. In other words: The other script languages I don't understand ;-)

Comment from Blogger , 11 months later

My professor told me: Many futurists would agree that, had it not been for symmetric encryption, the refinement of suffix trees might never have occurred. Here, we verify the analysis of spreadsheets, which embodies the important principles of algorithms. We explore an ambimorphic tool for studying wide-area networks, which we call TidEffort. This is an important point to understand.

Comment from morris , 11 months later

Paul: The "german" links are spam comments - link farming. Someone has obviously written a bot that can reasonably reliably read captcha texts.

Looks to be the same bot that was spamming my site (I recognise the cableguy link, and the patterns used for the comment text).

I replaced my captcha with a simple question and haven't had any more spam comments.

Comment from Morris , 11 months later

A tidier Javascript implementation. Javascript doesn't have a standard console, so windows wsh is used. Save the code to reverse.js and then from the windows command shell run: cscript reverse.js

function flip(ar,n) { return ar.slice(0,n).reverse().concat(ar.slice(n,ar.length)); }

var goal = [1,2,3,4,5,6,7,8,9]; var numbers = goal; var steps = 0;

for (var i = 0; i < goal.length; ++i) { numbers = flip(numbers, Math.ceil(Math.random() * goal.length) ); }

while (numbers.join('') != goal.join('')) { WScript.StdOut.Write(numbers.join(' ') + '\nHow many numbers to swap? '); numbers = flip(numbers, WScript.StdIn.ReadLine()); steps++; }

WScript.StdOut.WriteLine('Done! That took you ' + steps + ' steps!');

Comment from Morris , 11 months later

Sorry, using proper markdown (I hope!):

function flip(ar,n) {
  return ar.slice(0,n).reverse().concat(ar.slice(n,ar.length));
}

var goal = [1,2,3,4,5,6,7,8,9];
var numbers = goal;
var steps = 0;

for (var i = 0; i < goal.length; ++i) {
  numbers = flip(numbers, Math.ceil(Math.random() * goal.length) );
}

while (numbers.join('') != goal.join('')) {
  WScript.StdOut.Write(numbers.join(' ') + '\nHow many numbers to swap? ');
  numbers = flip(numbers, WScript.StdIn.ReadLine());
  steps++;
}

WScript.StdOut.WriteLine('Done! That took you ' + steps + ' steps!');
Comment from Olivier Mengué , 12 months later

I just blogged about my implementation of Reverse in Cmd.exe (see the code above).

Comment from Köln , 12 months later

Very interesting, but it dont work.. Dominik, have you already found something?

Comment from js , 12 months later

There are other worlds .... 3 lines solution in k (www.kx.com) explanation: sequence unsorted if not every number is greater than its predecessor n _draw -p generates n random numbers from 0..p-1 without repeated elems (|n#x),n _ x = (reverse n take x) concat (n drop x) c m\x = while c(x) is true, new x is m(x) unsorted(try)\x = while unsorted(x), new x is try x

unsorted:~&/>': try:{0:(5:x; "Reverse how many? "); n:0$0:; (|n#x),n _ x} `0:"Done! That took you ",($-1+#unsorted(try)\1+9_draw -9)," steps"

Comment from js , 12 months later

bad formatting.. The code is

unsorted:~&/>’:

try:{0:(5:x; "Reverse how many? "); n:0$0:; (|n#x),n _ x}

`0:”Done! That took you “,($-1+#unsorted(try)\1+9_draw -9),” steps”

Comment from Sven , 13 months later

Hi, please can you add a summary next time, so that your visitors can get an overview of the end-length of every code? Thank you.

Comment from Online Versicherungsvergleiche , 13 months later

I prefer the code in JavaScript (like morris tidier Javascript implementation). That works good and sure.

Comment from Internetmarketing , 13 months later

I really like PHP and don´t understand nothing about the snake (python) but in my childhood i programmed with Basic too i enjoyed it much it was so easy but today the computerlanguages are so much more powerfull but also complicated. i´m looking back to the good old "Basic"-times.

Comment from Paul , 13 months later

I generally think of myself as too jaded to spend much time trying to understand the chaotic patterns of spam, but I have got to know why this post is the only one out of the 300+ posts on my blog that gets these German/Eastern-European one-liners with links to big boring German/Eastern-European company websites.

Again, this may seem like a bit of a non-sequitur since I've deleted most of the comments that appeared to be spam. The mystery remains, though.

Comment from Dan , 14 months later

hi paul, i think it´s because of the most are just lazy. one have found a good way to become a backlink from your good blog. the others just look in "backlinkcheckers" of good ranking pages and find the backlink from this article and try to do the same. but maybe one have just searched for backlinks and made a list which he sell over eBay. its crazy at all the most time i think twice if i add a comment i don´t want that it seems i spam too. but i know u can delete comments and thats the reason i added this here.

head up, i think in future it will be better, the software is gonna be much better to prevent spam. i hope so :-)

Comment from Paul , 14 months later

Hey, a non-spam comment from Germany! Thanks, Dan!

I suspect you're right about the backlink business.

Comment from Martin Hamann , 14 months later

genius: "From another angle, all three programs would benefit from being a few lines longer. Based on pure principles, if nothing else."

i am a webmaster...Lol

Comment from Mediation Bernadette , 14 months later

Hey, a non-spam comment from Germany! Thanks, Dan! LOL

PS. Don't forget Poland, Paul :)

Comment from Paul , 14 months later

I always get Poland and Perl confused.

Comment from Tomek , 15 months later

Hey, a non-spam comment from Poland! Thanks for article. I don't think that all comments from poland are spam! And not all comments from other country. It's bigger problem people from USA buy .pl domains and spam people from DE buy .pl domains and spam ... So please don't write that people from poland spam here.

Comment from Joe , 16 months later

thaaanks!! i've been searching for 2h for such a scribt.

Comment from Gabe , 16 months later

Another VBScript implementation. Needed two custom procedures since VBScript does not have a Sort() method, or a way to reverse elements of an array (I compensated by converting array to string and reversing).

Randomize

Dim numbers(8)
For i = 0 To 8
    numbers(i) = Int((9 * Rnd) + 1)
Next

steps = 0

Do While Join(numbers) <> Join(Sort(numbers))
    WScript.Echo Join(numbers) & vbCrLf & "Reverse how many? "
    flipcount = WScript.StdIn.ReadLine
    ArrReverse numbers, flipcount
    steps = steps + 1
Loop

WScript.Echo "Done! That took you " & steps & " steps." & vbCrLf

Sub ArrReverse(array, intCount)

    str = Join(array)
    tmp = Split(StrReverse(Mid(str, 1, intCount * 2 - 1)) & Mid(str, intCount * 2))

    For i = 0 To 8
        array(i) = tmp(i)
    Next

End Sub

Function Sort(ByVal array)

    For i = UBound(array) - 1 To LBound(array) Step -1
    For j = 0 To i
        If array(j) > array(j + 1) Then
            temp = array(j)
            array(j) = array(j + 1)
            array(j + 1) = temp
        End If
    Next
    Next

    Sort = array

End Function
Comment from witze , 16 months later

very interesting comparison!

Comment from Leba , 16 months later

The big advantage of Perl is that you can write almost everything in only few lines. But after few weeks it is very hard to read. I wrote one project (using GoogleAPI) at my university, it takes 20 lines of code. But when 3 weeks later I was presenting this program to my professor, it took me some time to understand, what I have wriiten :D So...I don't recommend so short programs :D

Comment from Warsow , 16 months later

That oncemore shows that Ruby and Python is better than PHP :) thanks for one more proof. nice test

Comment from nitro , 16 months later

@Warsow one task shows you which language is the best? oh no.....

Comment from Darmowa bramka sms , 16 months later

Python version could be somewhat shorter.

numbers[:flipcount] = numbers[flipcount-1::-1]

I find this actually clearer than reversed()..

Comment from Wyniki na &#380;ywo , 17 months later

Hello, very interesting comparison!!!

Comment from Markus , 17 months later

Yes i think to that this is a vera interesting comparison.

Comment from Conti Correnti , 17 months later

Good job! I like the game, found it from another post with it written in a few different languages. Yours is by far the most complete and enjoyable.

Thanks!

Jeremy

Comment from Vit , 17 months later

"Ruby. For simple things at least!"

and PHP vs Ruby i think Ruby ist very " Speed" ;-)

Comment from Spiele , 17 months later

Good comparison. I saw something similar but with C, C++ and Ruby. I'll have to blog about it

Comment from jjkim , 17 months later

You can save 3 more characters in Python like this.

exec"print "+numbers[1:-1]

Looks ugly but it works. :)

Comment from jjkim , 17 months later

You can save 3 more characters in Python like this.

exec"print"+`numbers`[1:-1]

Looks ugly but it works. :)

Ignore my upper comment.

Comment from the spamer-hater , 17 months later

BIG LOOOOOOOLLLLLLLLL @ TOMEK!!!!!

TOMEK WRITE: "Hey, a non-spam comment from Poland! Thanks for article. I don’t think that all comments from poland are spam! And not all comments from other country. It’s bigger problem people from USA buy .pl domains and spam people from DE buy .pl domains and spam … So please don’t write that people from poland spam here."

That are the Words of the biggest SPAMER from POLAND!

TOMEK = www.blog.noclegi-lubuskie.pl = www.profesjonalna-reklama.pl = many more ......

he SPAM everywhere - if you wanna laugh just get his profesjonalna-reklama domain and look in a backlink-checker. i think there is no blog and no wiki out there without spam from him.

TOMEK: "...So please don’t write that people from poland spam here."

LOL sorry Paul for this off-topic-post, as i read that from tomek i must write it :-) and for his great work i spend my backlink from this post for him too :-)

Comment from Thomas , Zauberer , 18 months later

Hi, thanks a lot for all the useful hints and pieces of advice, kind reetings

Comment from Steve , 18 months later

Don't know if this is an active topic, but I used it as an excuse to learn Groovy. Here's a groovy version of the app:

  list = [*0..9]
  Collections.shuffle(list, new Random())
  count = 0
  sortedList = new ArrayList(list).sort()
  while (list != sortedList) {
    print list.join(" ") + " - How many to flip: "
  flipCount = System.in.readLine().toInteger()
  list[0..flipCount-1] = list[0..flipCount-1].reverse()
  count++
  }
  println "It took you $count tries."
Comment from Paul , 18 months later

Hi Steve,

This thread seems to be evergreen. Thanks for posting!

Comment from Noosa Accommodation , 18 months later

Yet again proving that python and Ruby are better than PHP. Keep up the great work.

Comment from Noosa , 18 months later

Yet again proving that python and Ruby are better than PHP. Keep up the great work.

Comment from Noosa , 18 months later

Great game. I have now bookmarked it. thanks

Comment from Poland Spammer , 19 months later

Tomek AKA Tomekg. Do a google search, you'll see how good he spams over the net. Just wondering how many hours he spends a day for spamming.

LOL --> esw.w3.org/topic/tomekg swik.net/User:tomekg

This guy is BIG spammer!

Comment from Spammer , 19 months later

Noosa - is spammer 3 comments of spam!

Comment from Paul , 19 months later

Maybe I should just block comments on this post that don't contain code.

Comment from Avi , 19 months later

Tcllib needed

Make the script a little smaller from previous version by using permutations for shuffle 2nd try to make this horrible markdown look OK...

package require math
package require struct::list

set input {1 2 3 4 5 6 7 8 9}
set perms [::struct::list permutations $input]
set myarray [lindex $perms [::math::random 0 [llength $perms]] ]
for {set steps 1}  {[::struct::list equal $myarray $input]==0} {incr steps} {
    puts -nonewline "$myarray \n($steps) How Many? "
    set howmany [gets stdin]
    set myarray [concat [::struct::list reverse [lrange $myarray 0 [expr $howmany-1]]] [lrange $myarray $howmany end]]
}
puts "$myarray \nDone in [expr $steps-1] steps"
Comment from Christopher Brown , 19 months later

Here's another Common Lisp example:

(defun play (n)
  (let* ((ordered-numbers (loop for i from 1 to n collect i))
         (numbers (sort (copy-seq ordered-numbers) #'(lambda (x y) (elt '(nil 0) (random 2))))))
    (loop for steps from 0 
          do
          (format t "~{~a~^, ~} : Reverse how many? " numbers)
          (let ((how-many (read)))
            (replace numbers (nreverse (subseq numbers 0 how-many)) :start1 0 :end1 how-many))
          until (equal numbers ordered-numbers) 
          finally (format t "Done in ~a steps!" steps))))
Comment from Leon , 19 months later

I made a bash version that, like the BASIC version, also takes several lines to randomize and reverse the prefix. I am a bash novice so maybe there are some built-in ways to shorten these parts. I'm almost afraid to know because I find the language so unlike what I'm used to. Here is a link because it's 42 lines and I don't trust my first try at Markdown to not screw it up: http://eloptoof.net/reverse.html

Some highlights:

Printing an array:

echo "${numbers[*]}"

(This also makes comparing two arrays a string compare.)

Prompting and reading a variable in one line:

read -p "Reverse how many? " rev

Array size:

n=${#numbers[@]}

I find most of the modern languages generally much easier to figure out. I am constantly checking the bash man page.

Comment from Graham J , 19 months later

PHP in 5 lines ;)

for($sorted=$numbers=range(1,9), shuffle($numbers), $steps=0; $numbers!=$sorted; $steps++) { echo implode(' ', $numbers)."\nReverse how many? "; array_splice($numbers, 0, $flipcount=(int)trim(fgets(STDIN)), array_reverse(array_slice($numbers,0,$flipcount))); } print "Done! That took you $steps steps.\n";

Comment from Graham J , 19 months later

PHP in 5 lines, Markdown version:

for($sorted=$numbers=range(1,9), shuffle($numbers), $steps=0; $numbers!=$sorted; $steps++) {
    echo implode(' ', $numbers)."\nReverse how many? ";
    array_splice($numbers, 0, $flipcount=(int)trim(fgets(STDIN)), array_reverse(array_slice($numbers,0,$flipcount)));
}
print "Done! That took you $steps steps.\n";
Comment from Sven , 20 months later

I prefer the code in Java-Script. That works good and sure.

Comment from Kre Alkalyn , 20 months later

Yes i think to that this is a very interesting comparison. Thx

Comment from Peter , 20 months later

Im learning a bit of Ruby after some hard PHP Lessons. As someone said above i would, at my actual point of knowledge, prefer Ruby rather than python...

Comment from Dan , 2 years later

Here's a Python hack: 6 lines and 311 bytes.

import random
def guesses(numbers):
    if numbers != sorted(numbers):
        flipcount = int(raw_input("%s\nReverse how many? " % (numbers,)))
        yield 1 + sum(guesses(numbers[flipcount - 1::-1] + numbers[flipcount:]))
print "Done! that took you %d steps." % sum(guesses(random.sample(range(1, 10), 9)))

Only works in Python 2.4 and later.

Comment from Paddy3118 , 2 years later

It seems that most implementations might be defective in that your initial randomization routines could produce a sorted list. the test on the outer while loop is on if the list is not sorted which would then fail, causing the programs to not ask for a reversal at all!

You would need to scramble the initial list of numbers until they are not sorted, then go on to the while loop.

  • Paddy.
Comment from rugs , 2 years later

No way, I remember coding in BASIC back when I was a kid with my brother. We used to love it. I think we did some pretty cool stuff back in those days as well, and almost got a full game working or something. It was great and so easy that even kids could do it – if Ruby and Python is the same (in terms of ease) I really wonder why I haven't actually gotten into it and seen what I can do. Heck, if I could program my own applications for my Linux system, that would super sweet!

Comment from gutscheine , 2 years later

Hi,

i don't think that php looks ugly. it's just where you came from.

i started coding with C ... so i'm familiar with the C-style and for me every language that doesn't use c-style bracketc etc. looks ugly :)

Comment from Max , 2 years later

And the winner is:

REBOL

Comment from Jeremy , 3 years later

Here is a version in Euphoria (8 lines, 317 bytes):

include std/console.e include std/sequence.e

object steps=1, gboard={1,2,3,4,5,6,7,8,9}, board=shuffle(gboard)

while not equal(gboard, board) do board = reverse(board, 1, prompt_number(join(board+48) & "\nReverse how many? ", {1,9})) steps += 1 end while

puts(1, sprintf("Done! That took you %d steps.", steps))

Euphoria is at: http://openeuphoria.org

Comment from Quady Trojmiasto , 3 years later

Heh, what a brain wash:)but the comparision is stunning, everybody good job in commitment;)

Comment from spiele , 4 years later

Great games, by the way this was a question in the college exam, I wish i saw this page before.

Comment from Phil Conrod , 4 years later

Below is the REVERSE game re-written in Microsoft Small Basic. It is from the New 2010 BASIC COMPUTER GAMES - SMALL BASIC EDITION which was recently republished by David H. Ahl and BIBLEBYTE BOOKS. And yes, David H. Ahl is still alive and kicking!

TextWindow.CursorLeft = 32 TextWindow.WriteLine("REVERSE") TextWindow.CursorLeft = 15 TextWindow.WriteLine("") TextWindow.WriteLine("") TextWindow.WriteLine("") TextWindow.WriteLine("REVERSE -- A GAME OF SKILL") TextWindow.WriteLine("") ' N=NUMBER OF NUMBERS N=9 TextWindow.Write("DO YOU WANT THE RULES? ") AD = TextWindow.Read() If AD="NO" Then Goto LN210 EndIf Sub710() ' MAKE A RANDOM LIST Array[1) TO Array[N) LN210: Array[1]=Math.Floor((N-1)Math.GetRandomNumber(999)/1000+2) For K=2 To N LN230: Array[K]=Math.Floor(NMath.GetRandomNumber(999)/1000+1) For J=1 To K-1 If Array[K]=Array[J] Then Goto LN230 EndIf EndFor EndFor ' PRINT ORIGINAL LIST AND START GAME) TextWindow.WriteLine("") TextWindow.WriteLine("HERE WE GO ... THE LIST IS: ") T=0 Sub610() LN330: TextWindow.Write("HOW MANY SHALL I REVERSE? ") R = TextWindow.ReadNumber() If R=0 Then Goto LN520 EndIf If R<=N Then Goto LN390 EndIf TextWindow.WriteLine("OOPS! TOO MANY! I CAN REVERSE AT MOST "+N) Goto LN330 LN390: T=T+1 ' REVERSE R NUMBERS AND PRINT NEW LIST) For K=1 To Math.Floor(R/2) Z=Array[K] Array[K]=Array[R-K+1] Array[R-K+1]=Z EndFor Sub610() ' *** CHECK FOR A WIN For K=1 To N If Array[K]<>K Then Goto LN330 EndIf EndFor TextWindow.WriteLine("YOU WON IT IN "+T+" MOVES!!!") TextWindow.WriteLine("") LN520: TextWindow.WriteLine("") TextWindow.Write("TRY AGAIN (YES OR NO)? ") AD = TextWindow.Read() If AD="YES" Then Goto LN210 EndIf TextWindow.WriteLine("") TextWindow.WriteLine("O.K. HOPE YOU HAD FUN!!") Goto LN999 ' SUBROUTINE TO PRINT LIST) Sub Sub610 TextWindow.WriteLine("") For K=1 To N TextWindow.Write(text.Append(" ",Array[K])) EndFor TextWindow.WriteLine("") TextWindow.WriteLine("") EndSub ' SUBROUTINE TO PRINT THE RULES) Sub Sub710 TextWindow.WriteLine("") TextWindow.WriteLine("THIS IS THE GAME OF 'REVERSE'. TO WIN, ALL YOU HAVE") TextWindow.WriteLine("TO DO IS ARRANGE A LIST OF NUMBERS (1 THROUGH "+N+")") TextWindow.WriteLine("IN NUMERICAL ORDER FROM LEFT TO RIGHT. TO MOVE, YOU") TextWindow.WriteLine("TELL ME HOW MANY NUMBERS (COUNTING FROM THE LEFT) TO") TextWindow.WriteLine("REVERSE. FOR EXAMPLE, IF THE CURRENT LIST IS:") TextWindow.WriteLine("") TextWindow.WriteLine("2 3 4 5 1 6 7 8 9") TextWindow.WriteLine("") TextWindow.WriteLine("AND YOU REVERSE 4, THE RESULT WILL BE:") TextWindow.WriteLine("") TextWindow.WriteLine("5 4 3 2 1 6 7 8 9") TextWindow.WriteLine("") TextWindow.WriteLine("NOW IF YOU REVERSE 5, YOU WIN!") TextWindow.WriteLine("") TextWindow.WriteLine("1 2 3 4 5 6 7 8 9") TextWindow.WriteLine("") TextWindow.WriteLine("NO DOUBT YOU WILL LIKE THIS GAME, BUT") TextWindow.WriteLine("IF YOU WANT TO QUIT, REVERSE 0 (ZERO).") TextWindow.WriteLine("") EndSub LN999:

Source: http://computerscienceforkids.com/SmallBasicComputerGames.aspx

Comment from Fotograf Düsseldorf , 4 years later

More traditionally the way to reverse list elements in Python is

numbers[:flipcount] = numbers[flipcount-1::-1]

for example

x=[1,2,3,4,5] i=2 x[:i] = x[i-1::-1] x [2, 1, 3, 4, 5] i=4 x[:i] = x[i-1::-1] x [4, 3, 1, 2, 5]

Comment from Web_Design Hamburg , 5 years later

to understand good post, but not simply

Comment from paul , 5 years later

i don't think that php looks ugly. it's just where you came from.

Comment from Zauberer NRW , 5 years later

My favorite code is the Code in the Java Script. It works eccellent and very save

Comment from Nick , 5 years later

Somehow I find myself back here five years later.

I consider myself a wiser Javascripter than I was, so I wrote it again in Javascript for fun, because there clearly are not enough Javascript implementations. I'm not even sure if this blog is still active, but in any case:

Javascript has no array shuffle, so I had to write that, and it also has no notion of array elementwise equality, so rather than writing that I just wrote an isSorted predicate function, obviating the need for a presorted goal array.

http://thelivegameshq.com/public/the-site/revgame-sync.html

Use "view source" to check it out. It's a polyglot HTML-slash-plain-Javascript file and uses environment sniffing to enable it to work both in a browser and via Windows' builtin JScript interpreter, which I used to test it.

It weighs in just north of a kilobyte but you could reduce that by 30% by taking away the comments and sniffing and using alert and prompt directly.

I then turned the game() function inside out to fit an async model so it could have a fancy HTML UI and also work in node.js (theoretically that is, I haven't tested it, which is as good as saying it doesn't):

http://thelivegameshq.com/public/the-site/revgame-async.html

About three kilobytes, but a third of that is the HTML and another third is the extended environment sniffing as well as a mock event loop because I didn't want to drop support for WSH. :) The actual game code that's left isn't changed much.

Cheers.

Comment from Nick , 5 years later

And then I notice Morris' years-old cute little way to compare arrays: by converting to strings.

Oh well.

Comment from medaids , 5 years later

This is working

x=[1,2,3,4,5] i=2 x[:i] = x[i-1::-1] x [2, 1, 3, 4, 5] i=4 x[:i] = x[i-1::-1] x [4, 3, 1, 2, 5]

Comment from hosplan , 5 years later

try this one. (defun play (n) (let* ((ordered-numbers (loop for i from 1 to n collect i)) (numbers (sort (copy-seq ordered-numbers) #'(lambda (x y) (elt '(nil 0) (random 2)))))) (loop for steps from 0 do (format t "~{~a~^, ~} : Reverse how many? " numbers) (let ((how-many (read))) (replace numbers (nreverse (subseq numbers 0 how-many)) :start1 0 :end1 how-many)) until (equal numbers ordered-numbers) finally (format t "Done in ~a steps!" steps))))

Comment from centurion , 5 years later

for($sorted=$numbers=range(1,9), shuffle($numbers), $steps=0;

If you use php it is easy as this

$numbers!=$sorted; $steps++) { echo implode(' ', $numbers)."\nReverse how many? "; array_splice($numbers, 0, $flipcount=(int)trim(fgets(STDIN)), array_reverse(array_slice($numbers,0,$flipcount))); } print "Done! That took you $steps steps.\n";

Comment from roman balick , 5 years later

interesting comparison between codes and very useful codes, thanks

Comment from furni Remo , 5 years later

Use "view source" to check it out. It's a polyglot HTML-slash-plain-Javascript file and uses environment sniffing to enable it to work both in a browser and via Windows' builtin JScript interpreter, which I used to test it.

It weighs in just north of a kilobyte but you could reduce that by 30% by taking away the comments and sniffing and using alert and prompt directly.

I then turned the game() function inside out to fit an async model so it could have a fancy HTML UI and also work in node.js (theoretically that is, I haven't tested it, which is as good as saying it doesn't):

Comment from Gett Taller , 5 years later

Just to enter below and check.

TextWindow.CursorLeft = 32 TextWindow.WriteLine("REVERSE") TextWindow.CursorLeft = 15 TextWindow.WriteLine("") TextWindow.WriteLine("") TextWindow.WriteLine("") TextWindow.WriteLine("REVERSE -- A GAME OF SKILL") TextWindow.WriteLine("") ' *** N=NUMBER OF NUMBERS

Comment from Smok Cigs , 5 years later

Regarding the codes above it is very easy to use this one below. nclude std/console.e include std/sequence.e

object steps=1, gboard={1,2,3,4,5,6,7,8,9}, board=shuffle(gboard)

while not equal(gboard, board) do board = reverse(board, 1, prompt_number(join(board+48) & "\nReverse how many? ", {1,9})) steps += 1 end while

puts(1, sprintf("Done! That took you %d steps.", steps))

Comments are closed for this post. But I welcome questions/comments via email or Twitter.