Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

SimpleRL on github

235 views
Skip to first unread message

Nik Coughlin

unread,
Feb 8, 2011, 9:40:03 PM2/8/11
to
Mainly because I wanted to figure out how to use github:
https://github.com/nrkn/SimpleRL

Nik Coughlin

unread,
Feb 9, 2011, 1:50:35 AM2/9/11
to
On Feb 9, 3:40 pm, Nik Coughlin <nrkn....@gmail.com> wrote:
> Mainly because I wanted to figure out how to use github:https://github.com/nrkn/SimpleRL

Working on FreePascal version at the moment.

At the first real programming job I had we used Delphi and it's
absolutely tragic how much I've forgotten :(

Konstantin Stupnik

unread,
Feb 9, 2011, 2:32:17 AM2/9/11
to
Ah. Delphi. I'm missing these days.
IMO Delphi 3 was perfect RAD tool for windows.
I even tried to make rl with it.

Nik Coughlin

unread,
Feb 10, 2011, 1:17:47 AM2/10/11
to

Abandoned FreePascal version. Was complaining that TStringList wasn't
a type or something... wtf.

Anyway, anyone want to contribute an implementation in a different
language?

Also considering forking a roguelike golf game from the repo, try and
get smallest (bytes not chars) implementation with same functionality

copx

unread,
Feb 12, 2011, 12:59:48 PM2/12/11
to
"Nik Coughlin" wrote in message
news:4b8882ac-525e-4047...@u14g2000vbg.googlegroups.com...

>Anyway, anyone want to contribute an implementation in a different
>language?

The problem with this is that most languages do not include
arbitrary console I/O in their standard libraries.

I wrote a C version, but this depends on the non-standard conio
library. You can actually compile and run this on a 80286 running
DOS, but a lame 64-bit Windows 7 workstation will work, too ;)

#include <conio.h>

typedef struct { int x; int y; } Point;

char Map[] = "#### ####\r\n"
"# #### #\r\n"
"# #\r\n"
"## ##\r\n"
" # # \r\n"
" # # \r\n"
"## ##\r\n"
"# #\r\n"
"# #### #\r\n"
"#### ####\r\n";

Point P = {2, 2};

void P_(int c) { gotoxy(1 + P.x, 1 + P.y); putch(c); }

void move(int x, int y) { if (Map[y * 12 + x] == ' ') { P.x = x; P.y = y; } }

void simple_rl(void)
{
clrscr(); cputs(Map);

P_('@');

{int key; while ((key = getch()) != 'q') {

P_(' ');

switch (key) {
case '8': move(P.x, P.y - 1); break;
case '2': move(P.x, P.y + 1); break;
case '4': move(P.x - 1, P.y); break;
case '6': move(P.x + 1, P.y); break;
}

P_('@');
}}
}

int main(void) { simple_rl(); return 0; }


Ido Yehieli

unread,
Feb 12, 2011, 1:31:58 PM2/12/11
to
On 02/12/2011 18:59 , copx wrote:
> "Nik Coughlin" wrote in message
> news:4b8882ac-525e-4047...@u14g2000vbg.googlegroups.com...
>> Anyway, anyone want to contribute an implementation in a different
>> language?
>
> The problem with this is that most languages do not include
> arbitrary console I/O in their standard libraries.
>
> I wrote a C version, but this depends on the non-standard conio
> library. You can actually compile and run this on a 80286 running
> DOS, but a lame 64-bit Windows 7 workstation will work, too ;)


Would this work on linux/mac?

-Ido.

Sherm Pendley

unread,
Feb 12, 2011, 1:48:09 PM2/12/11
to
Ido Yehieli <ido.y...@gmail.com> writes:

Not without some sort of emulation or wrapper - conio is DOS & Windows-
specific.

sherm--

--
Sherm Pendley
<http://camelbones.sourceforge.net>
Cocoa Developer

copx

unread,
Feb 12, 2011, 1:50:10 PM2/12/11
to

"Ido Yehieli" wrote in message news:6528b$4d56d21e$5472863d$31...@news.chello.at...

>Would this work on linux/mac?

Except for the conio stuff it's pure ANSI C89, so it should
compile and run everywhere provided you link it against
a Turbo C style conio library. I am pretty sure you can
find a conio implementation for Linux, Mac OS X, or
whatever if you seriously look for it.

As I said there is no standard way to do this in C, you
have to rely on one non-standard library or the other,
an conio is the simple one.

copx

unread,
Feb 12, 2011, 2:25:00 PM2/12/11
to

"copx" wrote in message news:ij6hr1$r99$1...@speranza.aioe.org...

>I wrote a C version, but this depends on the non-standard conio
>library. You can actually compile and run this on a 80286 running
>DOS, but a lame 64-bit Windows 7 workstation will work, too ;)

Minor tweak, but please use this version:

Todd Carnes

unread,
Feb 12, 2011, 4:10:00 PM2/12/11
to

I think you have to replace the conio stuff with ncurses stuff on Linux.

Todd

Filip Dreger

unread,
Feb 12, 2011, 6:20:58 PM2/12/11
to
Cool. As soon as I figure out how to use Github, I will commit my Java version. As Java doesn't have a standard console library, I made it into an applet (a working version is here: http://bajobongo.net/rl/)

Idiomatic Java is verbose, but it's shorter than I thought it would be (the length is mainly due to implementing own curses on top of Swing):

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class RL extends JApplet {
String map = "#### ####\n"
+ "# #### #\n"
+ "# #\n"
+ "## ##\n"
+ " # # \n"
+ " # # \n"
+ "## ##\n"
+ "# #\n"
+ "# #### #\n"
+ "#### ####\n";
int x = 5, y = 5;

JTextArea pane = new JTextArea();

public RL() throws HeadlessException {

// create GUI that predends to be a console
setContentPane(pane);
pane.setEditable(false);
pane.setFont(new Font("monospaced",Font.PLAIN,14));

// kickstart display
repaintMap();

// key listener
pane.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()){
case KeyEvent.VK_LEFT: moveHero (-1,0); break;
case KeyEvent.VK_RIGHT: moveHero (1,0); break;
case KeyEvent.VK_UP: moveHero (0,-1); break;
case KeyEvent.VK_DOWN: moveHero (0,1); break;
}
repaintMap();}
});
}

private int indexFromCoords(int x, int y){
return y * (map.indexOf("\n") + 1) + x;
}

private void moveHero(int dx, int dy) {
if (map.charAt(indexFromCoords(x + dx, y + dy)) == ' ') {
x = x + dx;
y = y + dy;
}
}

private void repaintMap(){
char[] currentMap = map.toCharArray();
currentMap[indexFromCoords(x, y)] = '@';
pane.setText(new String(currentMap));
}

}


Filip

narf_the_mouse

unread,
Feb 13, 2011, 4:11:55 AM2/13/11
to

It may be helpful to hightlight changes. :)

Nik Coughlin

unread,
Feb 13, 2011, 3:16:41 PM2/13/11
to
On Feb 13, 8:25 am, "copx" <c...@gazeta.pl> wrote:
> "copx"  wrote in messagenews:ij6hr1$r99$1...@speranza.aioe.org...

Hi,

Added at:

https://github.com/nrkn/SimpleRL/tree/master/c

Would like to see a curses version. Actually lack of (easy, minimal
effort, out of the box) curses on Windows is why I didn't do a Perl or
Python version. Ruby comes with curses even on Windows.

Nik Coughlin

unread,
Feb 13, 2011, 3:17:31 PM2/13/11
to
On Feb 13, 12:20 pm, Filip Dreger <fdre...@gmail.com> wrote:
> Cool. As soon as I figure out how to use Github, I will commit my Java version. As Java doesn't have a standard console library, I made it into an applet (a working version is here:http://bajobongo.net/rl/)

Thanks

Added:

https://github.com/nrkn/SimpleRL/tree/master/java

narf_the_mouse

unread,
Feb 13, 2011, 5:39:39 PM2/13/11
to

...Which isn't as easy as I thought. Comment withdrawn.

narf_the_mouse

unread,
Feb 13, 2011, 5:49:19 PM2/13/11
to
On 08/02/2011 6:40 PM, Nik Coughlin wrote:
> Mainly because I wanted to figure out how to use github:
> https://github.com/nrkn/SimpleRL

Added front and back draw buffers; generic'd the map and character
drawing to "WriteToBuffer".


using System;
using System.Collections.Generic;

namespace BasicEngine
{
class Program
{
static readonly List<string> Map = new List<string>{
"#### ####",
"# #### #",
"# #",
"## ##",
" # # ",
" # # ",
"## ##",
"# #",
"# #### #",
"#### ####"
};

//current draw buffer
static int curDrawBuffer = 0;
//draw buffer
static char[, ,] drawBuffer = new char[2, 10, 10];


static int _playerX = 2, _playerY = 2;

static void Main()
{
//clear the console
Console.Clear();

//write the map to the draw buffer
WriteToBuffer(Map);
//write the character to the draw buffer
WriteToBuffer('@', _playerX, _playerY);
//draw the draw buffer
Draw();

ConsoleKey key;

//keep processing key presses until the player wants to quit
while ((key = Console.ReadKey(true).Key) != ConsoleKey.Q)
{
//change the player's location if they pressed an arrow key
switch (key)
{
case ConsoleKey.UpArrow:
MovePlayer(_playerX, _playerY - 1);
break;
case ConsoleKey.DownArrow:
MovePlayer(_playerX, _playerY + 1);
break;
case ConsoleKey.LeftArrow:
MovePlayer(_playerX - 1, _playerY);
break;
case ConsoleKey.RightArrow:
MovePlayer(_playerX + 1, _playerY);
break;
}

//draw the map
WriteToBuffer(Map);

//now draw the player at the new square
WriteToBuffer('@', _playerX, _playerY);

//draw the draw buffer
Draw();
}
}

static void MovePlayer(int newX, int newY)
{
//don't move if the new square isn't empty
if (Map[newY][newX] != ' ') return;

//set the new position
_playerX = newX;
_playerY = newY;
}


private static void WriteToBuffer(List<string> Map)
{
//update the current draw buffer with the map chars.
int x, y;
string line;
for (y = 0; y < Map.Count; ++y)
{
//get current line
line = Map[y];
for (x = 0; x < line.Length; ++x)
{
//set current character
drawBuffer[curDrawBuffer, x, y] = line[x];
}
}
}


static void WriteToBuffer(char tile, int posX, int posY)
{
//write the char to the draw buffer
drawBuffer[curDrawBuffer, posX, posY] = tile;
}


private static void Draw()
{
//perform a quick blitter operation
int x, y, backDrawBuffer = 1 - curDrawBuffer;
for (y = 0; y < drawBuffer.GetLength(2); ++y)
{
for (x = 0; x < drawBuffer.GetLength(1); ++x)
{
if (drawBuffer[curDrawBuffer, x, y] !=
drawBuffer[backDrawBuffer, x, y])
{ Console.SetCursorPosition(x, y);
Console.Write(drawBuffer[curDrawBuffer, x, y]); }
}
}
curDrawBuffer = 1 - curDrawBuffer;
}
}
}

alex23

unread,
Feb 13, 2011, 10:24:00 PM2/13/11
to
Nik Coughlin <nrkn....@gmail.com> wrote:
> Would like to see a curses version. Actually lack of (easy, minimal
> effort, out of the box) curses on Windows is why I didn't do a Perl or
> Python version. Ruby comes with curses even on Windows.

It looks like there's active effort to get curses working under
Windows with Python 3.x: http://bugs.python.org/issue2889

There's a wrapper around PDCurses for 2.5 to 3.2:
http://www.lfd.uci.edu/~gohlke/pythonlibs/#curses

The latter I wasn't aware of before today, which makes me keen to see
whether it plays nicely with Urwid: http://excess.org/urwid/

If installing a single dependency isn't outside what you're aiming for
with SimpleRL, I'd be happy to do up a minimal Python version using
the above.

Nik Coughlin

unread,
Feb 13, 2011, 10:55:52 PM2/13/11
to

That would be awesome, just include a readme saying what the
dependency is

copx

unread,
Feb 14, 2011, 3:52:45 AM2/14/11
to
"Nik Coughlin" wrote in message
news:b9db2f40-aef6-439a...@8g2000prt.googlegroups.com...

>https://github.com/nrkn/SimpleRL/tree/master/c

Somehow the formatting got messed up, please change the following:
Line 5: remove one space after the '='
Line 39: shouldn't be there. Remove it.

This is supposed to illustrate the beauty of C, so formatting matters ;)

>Would like to see a curses version.

Sorry, I won't do it. I strongly dislike curses. A large fraction of the code
would consist of nothing but curses bookkeeping, which would only
demonstrate the verbose uglyness of curses, not the concise beauty of C.

>Actually lack of (easy, minimal effort, out of the box) curses on Windows
>is why I didn't do a Perl or Python version.

The Windows console is not meant to be used as a gaming platform
anyway. "Textmode" was great back in the DOS days, and I guess it's
still a decent enough choice if you use a suitable vterm on a UNIX-like,
but it's ugly on Windows. I always use SDL these days because of that.


batyann811

unread,
Feb 14, 2011, 8:38:18 AM2/14/11
to
Le Wed, 09 Feb 2011 22:17:47 -0800, Nik Coughlin a écrit :

>
> Abandoned FreePascal version. Was complaining that TStringList wasn't a
> type or something... wtf.
>
> Anyway, anyone want to contribute an implementation in a different
> language?
>

FreePascal version :

program SimpleRL;

uses Crt;

type
TPoint = record
X, Y : Integer;
end;

const
Map : Array [1..10] of String[10] = ( '#### ####',


'# #### #',
'# #',
'## ##',
' # # ',
' # # ',
'## ##',
'# #',
'# #### #',

'#### ####');

var
P : TPoint = ( X: 2; Y: 2);
Key : Char;

procedure DrawTile(C: Char);
begin
GotoXY(P.X, P.Y);
Write(c);
end;

procedure MovePlayer(X, Y: Integer);
begin
if Map[Y][X] = ' ' then
begin
P.X := X;
P.Y := Y;
end;
end;

procedure DrawMap;
var
I : Integer;
begin
for I := Low(Map) to High(Map) do
WriteLn(Map[I]);
end;

begin
ClrScr;

DrawMap;

DrawTile('@');

repeat
Key := ReadKey;

DrawTile(' ');

case Key of
'8' : MovePlayer(P.X, P.Y - 1);
'2' : MovePlayer(P.X, P.Y + 1);
'4' : MovePlayer(P.X - 1, P.Y);
'6' : MovePlayer(P.X + 1, P.Y);
end;

DrawTile('@');
until Key = 'q';
end.


Nik Coughlin

unread,
Feb 14, 2011, 2:38:36 PM2/14/11
to


Hi, nice improvements, but ones that make it less simple so I won't be
putting them into the main SimpleRL branch. However if you don't mind
I'd like to use it as the base for building my next fork of the
project

Nik Coughlin

unread,
Feb 14, 2011, 2:49:59 PM2/14/11
to
On Feb 14, 9:52 pm, "copx" <c...@gazeta.pl> wrote:
> "Nik Coughlin"  wrote in message
>
> news:b9db2f40-aef6-439a...@8g2000prt.googlegroups.com...
>
> >https://github.com/nrkn/SimpleRL/tree/master/c
>
> Somehow the formatting got messed up, please change the following:
> Line 5: remove one space after the '='
> Line 39: shouldn't be there. Remove it.
>
> This is supposed to illustrate the beauty of C, so formatting matters ;)
>
> >Would like to see a curses version.
>
> Sorry, I won't do it. I strongly dislike curses. A large fraction of the code
> would consist of nothing but curses bookkeeping, which would only
> demonstrate the verbose uglyness of curses, not the concise beauty of C.

I agree that it's not pretty. But it's pretty much the standard
unfortunately.

If I get a curses version I think I'll have it alongside rather than
instead of the conio version.

> >Actually lack of (easy, minimal effort, out of the box) curses on Windows
> >is why I didn't do a Perl or Python version.
>
> The Windows console is not meant to be used as a gaming platform
> anyway. "Textmode" was great back in the DOS days, and I guess it's
> still a decent enough choice if you use a suitable vterm on a UNIX-like,
> but it's ugly on Windows. I always use SDL these days because of that.

I updated Github with your changes and deleted that blank line, but
there isn't an extra space after the =, it's their text formatter
that's screwy, if you download the file the formatting is fine.

Nik Coughlin

unread,
Feb 14, 2011, 2:50:12 PM2/14/11
to

Hi, added, thanks heaps.

narf_the_mouse

unread,
Feb 14, 2011, 3:37:04 PM2/14/11
to

Go ahead. :)

And, in one of those "Why did it take this long to think of this?"
things - Change:

>> Console.Write(drawBuffer[curDrawBuffer, x, y]); }

to:

>> Console.Write(drawBuffer[backDrawBuffer, x, y] =
drawBuffer[curDrawBuffer, x, y]); }

If something is only written once, the original line can cause it to
flicker between what was there originally and the new text. I'd been
fixing that with various hacks for years.

narf_the_mouse

unread,
Feb 14, 2011, 3:43:26 PM2/14/11
to
On 14/02/2011 11:49 AM, Nik Coughlin wrote:
> On Feb 14, 9:52 pm, "copx"<c...@gazeta.pl> wrote:
>> "Nik Coughlin" wrote in message
>>
>> news:b9db2f40-aef6-439a...@8g2000prt.googlegroups.com...
>>
>>> https://github.com/nrkn/SimpleRL/tree/master/c
>>
>> Somehow the formatting got messed up, please change the following:
>> Line 5: remove one space after the '='
>> Line 39: shouldn't be there. Remove it.
>>
>
> I updated Github with your changes and deleted that blank line, but
> there isn't an extra space after the =, it's their text formatter
> that's screwy, if you download the file the formatting is fine.

I just checked it again and I'm not seeing an extra space after the '='.
Maybe it's a browser problem? I'm on Firefox 3.6.

copx

unread,
Feb 14, 2011, 3:46:25 PM2/14/11
to
"Nik Coughlin" wrote in message
news:0df89256-2a45-4bfc...@o21g2000prn.googlegroups.com...

>I agree that it's not pretty. But it's pretty much the standard
>unfortunately.

Only on UNIX-likes. Curses is a UNIX terminal abstraction library,
much of it's functionality doesn't even make sense on Windows
(i.e. th platform 90%+ of all desktop users use). For example, curses
has a function you are expected to call to figure out if the terminal
supports color before you try to initialize color pairs! That might make
sense if you actually want to support monochrome output on a UNIX
workstation from the 1970s, but the Windows console always supports
color. And that's just one example. Curses also introduces whole
new categories of bugs which are almost certain to bite you.
For example, forgetting to call "refresh()". I remember spending
half an hour trying to figure out why my code wasn't doing what it was
supposed to do until I realized that I forgot a "refresh()" somewhere.
Manual buffer management isn't any less tricky than manual memory
management and it's completely unnecessary. Borland's conio runs circles
around curses because those conio calls are compiled to direct hardware access
(on DOS, but it's also fast enough on Windows 9x)
In contrast, curses is supposed to deal with very slow UNIX terms where
you might need such buffering. And the Windows NT-line consoles
suck no matter what you try. They really aren't meant to be used for games.

Again, curses is NOT a standard on most desktop computers out
there. My conio example will actually build out-of-the-box with many compilers
e.g. Turbo C (DOS) or lccwin32 (Windows), you don't even have to
tell the compiler to link any particular library! In contrast, for curses on
non-UNIX platforms you need an emulation library (pdcurses), build that library
from source, copy its files to the correct locations, and finally tell your compiler to
link it against your program... Because it's not standard at all.

curses is alien matter on DOS/Windows/and OS/2. There "conio" is much closer
to a "standard".

>If I get a curses version I think I'll have it alongside rather than
>instead of the conio version.

I hope so, otherwise I would have wasted my time writing this
after all.

>I updated Github with your changes and deleted that blank line

Good, thanks.

Nik Coughlin

unread,
Feb 14, 2011, 10:38:35 PM2/14/11
to
On Feb 9, 3:40 pm, Nik Coughlin <nrkn....@gmail.com> wrote:
> https://github.com/nrkn/SimpleRL

Going well, it now has an implementation in:

c by copx
coffeescript by Will Moffat
corona by Tom Demuyt
csharp by me
freepascal by batyann811
fsharp by gradbot
java by Filip Dreger
js by Simon Oberhammer
powershell by me
python by Alex Dante
ruby by me

I suspect that the ruby version isn't terribly idiomatic, it's
(nearly) the first ruby I've ever written

To keep myself further amused, been playing code golf with it as well:
https://github.com/nrkn/SimpleRLGolf

I've done csharp (got it down to 384 bytes) and js (369).

The js size includes the html required to host it.

Ido Yehieli

unread,
Feb 15, 2011, 4:52:50 AM2/15/11
to
On 14.02.2011 21:46, copx wrote:
> "Nik Coughlin" wrote in message
> news:0df89256-2a45-4bfc...@o21g2000prn.googlegroups.com...
>> I agree that it's not pretty. But it's pretty much the standard
>> unfortunately.
>
> Only on UNIX-likes.

So pretty much any current OS except Windows? ;)

Linux, Mac OS X, *BSDs, Android, iOS, WebOS, ChromeOS...You'll be hard
pressed to find an os in common use beside windows that isn't a UNIX-like.

copx

unread,
Feb 15, 2011, 6:10:33 AM2/15/11
to

"Ido Yehieli" wrote in message news:ijdihp$3mo$1...@news.eternal-september.org...

>> Only on UNIX-likes.
>
>So pretty much any current OS except Windows? ;)

..which runs on almost every desktop i.e. the primary target platform
for most roguelikes.

>Linux, Mac OS X, *BSDs, Android, iOS, WebOS, ChromeOS...You'll be hard
>pressed to find an os in common use beside windows that isn't a UNIX-like.

Taking some code from Linux or BSD because it is free doesn't make
an OS a UNIX-like.

On Android the standard is Google's proprietary Java VM and class
library, on OS X it's Cocoa, on ChromeOS it's the web browers etc.

The the user/developer experience on these OSes has nothing in common
with the UNIX one. Otherwise nobody except the bearded guy in the
basement would use them! ;)

Ido Yehieli

unread,
Feb 15, 2011, 6:19:29 AM2/15/11
to
On 15.02.2011 12:10, copx wrote:
> "Ido Yehieli" wrote in message
>>> Only on UNIX-likes.
>>
>> So pretty much any current OS except Windows? ;)
>
> ..which runs on almost every desktop i.e. the primary target platform
> for most roguelikes.


We had this discussion before, rl players are a different demographics
than general computer users.

On tametick.com only 60% use windows, with the rest being a bit more mac
than linux, and apparently other roguelike oriented sites experienced
similar traffic.

-Ido.

copx

unread,
Feb 15, 2011, 6:45:45 AM2/15/11
to

"Ido Yehieli" wrote in message news:ijdnk8$sic$1...@news.eternal-september.org...

>We had this discussion before, rl players are a different demographics
>than general computer users.

That's true, but..

>On tametick.com only 60% use windows, with the rest being a bit more mac
>than linux, and apparently other roguelike oriented sites experienced
>similar traffic.

So even among hardcore roguelike fans Windows users are still the majority!
And those numbers don't represent the player base as a whole. There are
certainly way more Windows users among the less dedicated fans.

And of course OS X/Linux user doesn't mean curses user in the first place.
Tiles or "emulated ASCII" (i.e. what Dwarf Fortress does for example) are
much more popular. I dare to predict that out of all roguelikes being played
right now less than 10% use curses IO.


Ido Yehieli

unread,
Feb 15, 2011, 6:47:59 AM2/15/11
to
On 15.02.2011 12:45, copx wrote:
>
> And of course OS X/Linux user doesn't mean curses user in the first place.
> Tiles or "emulated ASCII" (i.e. what Dwarf Fortress does for example) are
> much more popular. I dare to predict that out of all roguelikes being
> played right now less than 10% use curses IO.
>

I dare to predict that far fewer (marginally more than 0%) use conio,
seeing as there are curses compatible libs for windows (and that is what
most ascii RLs on windows use).

copx

unread,
Feb 15, 2011, 7:12:48 AM2/15/11
to

"Ido Yehieli" wrote in message news:ijdp9m$grk$1...@news.eternal-september.org...

>> I dare to predict that out of all roguelikes being played right now less than 10%
>> use curses IO.
>
>I dare to predict that far fewer (marginally more than 0%) use conio,

I don't dispute that. The point was that claiming that curses is a roguelike
standard in this day and age is non-sense.

>seeing as there are curses compatible libs for windows (and that is what
>most ascii RLs on windows use).

Who plays console mode RLs on Windows? The only recent/popular
example I know is Crawl, and they didn't use curses but direct Win32 API
calls. ADOM, Angband, DoomRL, .. they all use "emulated ASCII" on Windows
(only sane choice really). Nethack? Maybe they use PDcurses I don't know.
But claiming "most ascii RLS on Windows use curses compatible libs"
is definitely wrong if you restrict yourself to roguelikes which are
actually being played, as opposed to simple "me too" roguelikes whose
developers where mislead to use curses.

Ido Yehieli

unread,
Feb 15, 2011, 7:34:26 AM2/15/11
to
On 15.02.2011 13:12, copx wrote:
>
> "Ido Yehieli" wrote in message
> news:ijdp9m$grk$1...@news.eternal-september.org...
>>> I dare to predict that out of all roguelikes being played right now
>>> less than 10% use curses IO.
>>
>> I dare to predict that far fewer (marginally more than 0%) use conio,
>
> I don't dispute that. The point was that claiming that curses is a
> roguelike
> standard in this day and age is non-sense.


I don't use actual console for my roguelikes either (anymore), but for
RLs that actually use the console it is closer to being the standard
than any other option.

Maybe I'm biased because I actually mostly play those little in-dev
roguelikes (I must have played dozens of them in 2010, albeit most not
for very long) and I almost never play "major" (ADOM, Nethack, Angband,
Crawl) roguelikes.

Darren Grey

unread,
Feb 15, 2011, 8:48:41 AM2/15/11
to
On Feb 15, 12:12 pm, "copx" <c...@gazeta.pl> wrote:
> "Ido Yehieli"  wrote in messagenews:ijdp9m$grk$1...@news.eternal-september.org...

I thought ADOM used curses? There's a "Windows" version, but the DOS
version or the emulated Linux version are probably more popular
overall.

I agree that curses shouldn't define a roguelike though. The screen
drawing interface is of trivial importance.

--
Darren Grey

konijn_

unread,
Feb 15, 2011, 10:37:34 AM2/15/11
to
On Feb 15, 7:12 am, "copx" <c...@gazeta.pl> wrote:
> "Ido Yehieli"  wrote in messagenews:ijdp9m$grk$1...@news.eternal-september.org...

Greetings, crawl still can compile to curses so that you can play it
over telnet, same for nethack and angband ( variants ).

T.

copx

unread,
Feb 15, 2011, 11:20:47 AM2/15/11
to

"konijn_" wrote in message news:d08fa7d8-19df-4de1...@u24g2000prn.googlegroups.com...


>Greetings, crawl still can compile to curses so that you can play it
>over telnet, same for nethack and angband ( variants ).

CAN, but the default version for the general public doesn't use
that. Angband is NOT curses-based, but only uses curses as one
of many back-ends (I know that code base). And the last time
I looked at the Crawl source code it wasn't curses-based either.

But I don't even know what we are arguing about here anymore.
Fact is curses is not any more part of C than conio. And as far
as I understand it this SimpleRL thing is supposed to showcase
different programming languages, not a particular IO library being
used by different programming languages.

conio's straight-forward simplicity mirrors that of C's
standard library and thus the code looks more like C is supposed
to look IMO. If you want, you can write a C + GTK (even worse than
curses) example, and convince everyone that they can be glad
one doesn't have to use C anymore.

If this were supposed to be a foundation for a real game one should
use neither curses nor conio but some wrapper.

Really, my time is valuable, I won't waste any more of it with
this. I have said everything I wanted to say.

Good bye.

Filip Dreger

unread,
Feb 15, 2011, 1:31:11 PM2/15/11
to
On Monday, February 14, 2011 9:46:25 PM UTC+1, copx wrote:
> curses is alien matter on DOS/Windows/and OS/2.

AAAAA!!! do NOT mention OS/2!!! They do not need any more encouraging!
Filip

konijn_

unread,
Feb 15, 2011, 1:39:43 PM2/15/11
to
<snip stuff where you agree with what I wrote but dispute the
relevance of it>

> If this were supposed to be a foundation for a real game one should
> use neither curses nor conio but some wrapper.

I agree.

> Really, my time is valuable, I won't waste any more of it with
> this. I have said everything I wanted to say.

I see you still have a short temper ;) Still it is good to see you
read this newsgroup !

>
> Good bye.

Cheers,
T.

Sherm Pendley

unread,
Feb 15, 2011, 4:39:00 PM2/15/11
to
Filip Dreger <fdr...@gmail.com> writes:

> On Monday, February 14, 2011 9:46:25 PM UTC+1, copx wrote:
>> curses is alien matter on DOS/Windows/and OS/2.
>

> AAAAA!!! do NOT mention [unmentionable deleted]!!! They do not need
> any more encouraging!

LOL!

sherm--

--
Sherm Pendley
<http://camelbones.sourceforge.net>
Cocoa Developer

jab

unread,
Feb 15, 2011, 5:32:41 PM2/15/11
to
On Feb 13, 12:16 pm, Nik Coughlin <nrkn....@gmail.com> wrote:
> Would like to see a curses version. Actually lack of (easy, minimal

> effort, out of the box) curses on Windows is why I didn't do a Perl or
> Python version. Ruby comes with curses even on Windows.

#include <curses.h>
const char *map =
"#### ####\n"
"# #### #\n"
"# #\n"
"## ##\n"
" # # \n"
" # # \n"
"## ##\n"
"# #\n"
"# #### #\n"
"#### ####\n";
int px = 1, py = 1;
void move_to(int x, int y) { if (map[y * 11 + x] == ' ') { px = x; py
= y; } }
void simple_rl(void)
{
int key;
initscr(); noecho(); curs_set(0); keypad(stdscr, 1);
mvaddstr(0, 0, map); mvaddch(py, px, '@');
while ((key = getch()) != 'q') {
mvaddch(py, px, ' ');
switch (key) {
case KEY_UP: move_to(px, py - 1); break;
case KEY_DOWN: move_to(px, py + 1); break;
case KEY_LEFT: move_to(px - 1, py); break;
case KEY_RIGHT: move_to(px + 1, py); break;
}
mvaddch(py, px, '@');
}
endwin();
}
int main(void) { simple_rl(); return 0; }

"Nik Coughlin" wrote in message

> Sorry, I won't do it. I strongly dislike curses. A large fraction of the code
> would consist of nothing but curses bookkeeping, which would only
> demonstrate the verbose uglyness of curses, not the concise beauty of C.

While I agree that the curses interface is quite ugly, I still don't
think it should be disregarded
so rashly. And CONIO is hardly (IMHO) a prime example of conciseness
and beauty. And as for bookkeeping,
I don't see any of that in the code I posted above. Just a few more
"initialization" functions.

David Damerell

unread,
Feb 16, 2011, 7:37:11 AM2/16/11
to
Quoting copx <co...@gazeta.pl>:
>Fact is curses is not any more part of C than conio. And as far
>as I understand it this SimpleRL thing is supposed to showcase
>different programming languages, not a particular IO library being
>used by different programming languages.

I think it's pretty clear that (unless people start writing, aha, complex
SimpleRL implementations) they inevitably showcase (language, IO library)
pairs, like it or not.

Given that we are doing that, it would be better to call yours (C, Windows
conio) and have done with it.
--
David Damerell <dame...@chiark.greenend.org.uk> flcl?
Today is Second Wednesday, February.
Tomorrow will be Second Thursday, February.

David Damerell

unread,
Feb 16, 2011, 8:29:57 AM2/16/11
to
Quoting Nik Coughlin <nrkn...@gmail.com>:
>Mainly because I wanted to figure out how to use github:
>https://github.com/nrkn/SimpleRL

#!/usr/bin/perl -w
# Copyright (C) 2011 David Damerell
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

use Curses;
if (initscr()) {
if (($COLS<20) || ($LINES <20)) {
die "Please use at least a 20x20 display";
}
} else {
die "Curses support not available?";
}
curs_set(0);cbreak;noecho;

@maplines=("#### ####",


"# #### #",
"# #",
"## ##",
"# #",
"# #",
"## ##",
"# #",
"# #### #",

"#### ####");

$xmax=0;
foreach $line (0..$#maplines) {
@{$map[$line]}=split //,$maplines[$line];
$xmax=length($maplines[$line]) if length($maplines[$line]) > $xmax;
}
$ymax=$#maplines; $xmax--;

$player{'x'}=$player{'y'}=2;
%directions=('h',[ -1,0 ],'j',[ 0,1 ],'k',[ 0,-1 ],'l',[ 1,0 ],
'y',[ -1,-1 ],'u',[ 1,-1 ],'b',[ -1,1 ],'n',[ 1,1 ]);

&redraw_map;

while ($c = getch) {
if (defined($directions{$c})) {
$oldx=$player{'x'}; $oldy=$player{'y'};
$newx=$player{'x'}+$directions{$c}[0];
$newy=$player{'y'}+$directions{$c}[1];
if ($map[$newy][$newx] eq ' ') {
$oldx=$player{'x'}; $oldy=$player{'y'};
$player{'x'}=$newx; $player{'y'}=$newy;
&redraw ($oldx, $oldy); &redraw ($newx,$newy); refresh;
} else {
# you bumped into a wall
}
} elsif ($c eq 'r') {
&redraw_map;
} elsif ($c eq 'q') {
endwin; exit;
} else {
# complain, unknown keypress
}
}

sub redraw_map {
foreach $x (0..$xmax) {
foreach $y (0..$ymax) {
&redraw ($x,$y);
}
}
refresh;
}

sub redraw {
my $x=shift; my $y=shift;
if ($x == $player{'x'} and $y ==$player{'y'}) {
attrset (A_BOLD | A_REVERSE);
addch ($y, $x, '@');
attrset (A_NORMAL);
} else {
addch ($y, $x, $map[$y][$x]);

copx

unread,
Feb 16, 2011, 8:30:34 AM2/16/11
to

"jab" wrote in message news:f80fd02d-7b6e-4ae5...@d23g2000prj.googlegroups.com...

> initscr(); noecho(); curs_set(0); keypad(stdscr, 1);
> mvaddstr(0, 0, map); mvaddch(py, px, '@');
> while ((key = getch()) != 'q') {
mvaddch(py, px, ' ');

Muwahahahahahhahh! Ok, I was determined not to post
in this thread anymore but this is just too good! There
are three bugs in the above code, nicely illustrating why
curses should be "disregarded rashly". First, you forgot
to call cbreak() before getch(). Curses does not guarantee
the initial state of the terminal and getch() will only
work as expected if the terminal is in cbreak() mode.
Then you forgot to call refresh(), exactly the kind of
bug I mentioned before in this thread. So at the
point of your getch call the player might not see
a thing on screen and that getch() call might not
return after a single keypress either. Oh, and you
forgot to clear the screen (no initscr() doesn't do
that).

Now you might say "but it works on my system" (i.e. your
personal combination of terminal + curses library version +
the state your terminal is in when you run the program),
but that just illustrates the "beauty" of curses. Curses
might be "portable", but such bugs are highly system specific.

>And as for bookkeeping, i don't see any of that in the code I

>posted above. Just a few more "initialization" functions.

..which you managed to get wrong. Introducing 3 bugs in a few
lines of code is an accomplishment, but I don't blame you, I blame
curses. And when I wrote "bookkeeping" I meant those initialization
functions and refresh (which you forgot).

David Damerell

unread,
Feb 16, 2011, 1:20:06 PM2/16/11
to
Quoting David Damerell <dame...@chiark.greenend.org.uk>:
>Quoting Nik Coughlin <nrkn...@gmail.com>:
>>Mainly because I wanted to figure out how to use github:
>>https://github.com/nrkn/SimpleRL
>%directions=('h',[ -1,0 ],'j',[ 0,1 ],'k',[ 0,-1 ],'l',[ 1,0 ],
> 'y',[ -1,-1 ],'u',[ 1,-1 ],'b',[ -1,1 ],'n',[ 1,1 ]);

Ooops, I seem to have accidentally added diagonal movement. Sorry.

Sherm Pendley

unread,
Feb 16, 2011, 2:34:24 PM2/16/11
to
David Damerell <dame...@chiark.greenend.org.uk> writes:

> &redraw_map;

*twitch*

Please don't use & to call subs - nobody is using Perl 4 these days,
and it's pretty unlikely that you specifically intended to bypass proto-
type validation here. :-)

Nik Coughlin

unread,
Feb 16, 2011, 5:39:51 PM2/16/11
to

Seperated the c folder into c/conio and c/curses and added. Thanks.

Nik Coughlin

unread,
Feb 16, 2011, 5:40:15 PM2/16/11
to
On Feb 17, 2:29 am, David Damerell <damer...@chiark.greenend.org.uk>
wrote:

Added, thanks

Antoine

unread,
Feb 17, 2011, 1:47:25 AM2/17/11
to
On Feb 16, 5:20 am, "copx" <c...@gazeta.pl> wrote:
> Really, my time is valuable, I won't waste any more of it with
> this. I have said everything I wanted to say.
>
> Good bye.

Yeah! copx is raging!!

A.

David Damerell

unread,
Feb 17, 2011, 2:12:08 PM2/17/11
to
Quoting Sherm Pendley <sherm....@gmail.com>:
>David Damerell <dame...@chiark.greenend.org.uk> writes:
>> &redraw_map;
>Please don't use & to call subs - nobody is using Perl 4 these days,
>and it's pretty unlikely that you specifically intended to bypass proto-
>type validation here. :-)

Hm. You may have a point, although it may take a while to bypass over a
decade of finger memory.


--
David Damerell <dame...@chiark.greenend.org.uk> flcl?

Today is Second Thursday, February.
Tomorrow will be Second Friday, February.

jab

unread,
Feb 17, 2011, 3:50:50 PM2/17/11
to
Here is a forth (gforth) version: (Could use some factoring)

10 constant width
10 constant height
create map
'# , '# , '# , '# , '. , '. , '# , '# , '# , '# ,
'# , '. , '. , '# , '# , '# , '# , '. , '. , '# ,
'# , '. , '. , '. , '. , '. , '. , '. , '. , '# ,
'# , '# , '. , '. , '. , '. , '. , '. , '# , '# ,
'. , '# , '. , '. , '. , '. , '. , '. , '# , '. ,
'. , '# , '. , '. , '. , '. , '. , '. , '# , '. ,
'# , '# , '. , '. , '. , '. , '. , '. , '# , '# ,
'# , '. , '. , '. , '. , '. , '. , '. , '. , '# ,
'# , '. , '. , '# , '# , '# , '# , '. , '. , '# ,
'# , '# , '# , '# , '. , '. , '# , '# , '# , '# ,

1 value playerx
1 value playery

: .player ( -- ) playerx playery at-xy '@ emit ;
: .tile ( i -- ) dup width /mod at-xy cells map + @ emit ;
: .map ( -- ) width height * 0 do i .tile loop ;

: can-walk? ( x y -- f ) width * + cells map + @ '. = ;
: walk ( x y -- ) 2dup can-walk? if to playery to playerx else 2drop
then ;
: process ( ekey -- )
ekey>fkey drop case
k-up of playerx playery 1- walk endof
k-down of playerx playery 1+ walk endof
k-left of playerx 1- playery walk endof
k-right of playerx 1+ playery walk endof
endcase ;
: draw ( -- ) .map .player form swap at-xy ;
: quit? ( ekey -- ? ) ekey>char if 'q = else drop false then ;
: game ( ) begin ekey dup quit? invert while process draw repeat
drop ;

page draw game page bye

Filip Dreger

unread,
Feb 18, 2011, 3:26:07 PM2/18/11
to
Thats the biggest FORTH program I have ever seen. The first impression is that it looks surprisingly modern and all DSLy. The second impression - it's very alien; I fail to find a single construction I could really say I understand :-)
FIlip

Sherm Pendley

unread,
Feb 18, 2011, 3:30:32 PM2/18/11
to
jab <xjabbe...@gmail.com> writes:

> Here is a forth (gforth) version:

Wow! I haven't written (or seen) any Forth code since I owned an 8-bit
Atari. Thanks for the trip down memory lane! :-)

Nik Coughlin

unread,
Feb 18, 2011, 7:04:00 PM2/18/11
to

Forth! Awesome. Adding now.

Nik Coughlin

unread,
Feb 18, 2011, 8:45:56 PM2/18/11
to
On Feb 15, 4:38 pm, Nik Coughlin <nrkn....@gmail.com> wrote:
> On Feb 9, 3:40 pm, Nik Coughlin <nrkn....@gmail.com> wrote:
>
> >https://github.com/nrkn/SimpleRL
>
> Going well, it now has an implementation in:
>
> c            by copx
> coffeescript by Will Moffat
> corona       by Tom Demuyt
> csharp       by me
> freepascal   by batyann811
> fsharp       by gradbot
> java         by Filip Dreger
> js           by Simon Oberhammer
> powershell   by me
> python       by Alex Dante
> ruby         by me

Have since added:

basic by me
c by jab
factor by Risto Saarelma
forth by jab
perl by David Damerell
php by me

Nik Coughlin

unread,
Feb 18, 2011, 11:11:20 PM2/18/11
to

...and

html by me

The HTML (no JavaScript!) version is very, very silly :)

narf_the_mouse

unread,
Mar 1, 2011, 12:04:26 PM3/1/11
to
On 08/02/2011 6:40 PM, Nik Coughlin wrote:
> Mainly because I wanted to figure out how to use github:
> https://github.com/nrkn/SimpleRL

How goes this? Anything new?

Nik Coughlin

unread,
Mar 1, 2011, 3:01:39 PM3/1/11
to

I've been busy on other things, but I added another BASIC dialect, the
1968 edition of Dartmouth BASIC, which was, ah, interesting to code in
to say the least :)

Also someone contributed a better Ruby version, and I started a
Fortran version but abandoned it due to lack of time.

We're up to something like 21 versions in 18 languages/dialects.

Would really like to see more languages, as always!

copx

unread,
Mar 2, 2011, 4:09:44 AM3/2/11
to

"Nik Coughlin" wrote in message

news:6f4d9aae-19ea-4641...@d23g2000prj.googlegroups.com...


>Would really like to see more languages, as always!

Here is something actually useful. Please try to get
Roguebasin to link to your project, because I don't
want this code to rot unseen on some random corner
of the internet. I implemented SimpleRL in C++ but
that's not the point. I implemented it on top of SDL,
demonstrating the "emulated ASCII" technique. So
this should be useful for newbies who want to do
that.
This code depends on a font bitmap, download here:
http://biron.sourceforge.net/font.bmp
It's the PDCurses SDL font I use in my current project,
one can replace that with a standard BIOS font from
the same collection to get the straight DOS look.
If you wonder, my current project
(http://biron.sourceforge.net/) is not a roguelike,
but a tactical RPG with perma-death, thus still
somewhat close.
Finally, because this is for newbies here is how
you compile an SDL app with straight MinGW (i.e. no MSYS):

g++ -Os SimpleRL.cpp -lmingw32 -lSDLmain -lSDL -mwindows

You will have to install SDL first of course.

== SimpleRL.cpp ==

#include <SDL/SDL.h>

class Map {
public: enum { Height = 10, Width = 10 };
const char *sectors;
};

class Player {
public: int x; int y;
void move(int x, int y);
};

class Screen {
public: void on();
void off();
void update();
void draw(int x, int y, char sym);
void draw(const Map& map);
private: SDL_Surface *surface;
SDL_Surface *font;
};

class Keyboard {
public: typedef enum { Esc = SDLK_ESCAPE, Up = SDLK_UP, Down = SDLK_DOWN,
Left = SDLK_LEFT, Right = SDLK_RIGHT } Key;
void on();
void off();
Key getKey();
};

class Font {
public: enum { Height = 16, Width = 8, Columns = 32, Rows = 8 };
static const char * const Bitmap;
};
const char * const Font::Bitmap = "font.bmp";

class Map Map = { "#### ####"
"# #### #"
"# #"
"## ##"
" # # "
" # # "
"## ##"
"# #"
"# #### #"
"#### ####"};

class Player Player = {2, 2};

class Screen Screen;

class Keyboard Keyboard;

class Font Font;


void SimpleRL(void)
{
Screen.draw(Map);
Screen.draw(Player.x, Player.y, '@');

for (;;) {
switch (Keyboard.getKey()) {
case Keyboard::Up: Player.move(Player.x, Player.y - 1); break;
case Keyboard::Down: Player.move(Player.x, Player.y + 1); break;
case Keyboard::Left: Player.move(Player.x - 1, Player.y); break;
case Keyboard::Right: Player.move(Player.x + 1, Player.y); break;
case Keyboard::Esc: goto QuitGame;
}
}

QuitGame:;
}

int main(int argc, char *argv[]) {

Screen.on();
Keyboard.on();

SimpleRL();

Keyboard.off();
Screen.off();

return EXIT_SUCCESS;
}


void Player::move(int new_x, int new_y)
{
if (Map.sectors[new_y * Map::Width + new_x] == ' ') {
Screen.draw(x, y, ' ');
x = new_x; y = new_y;
Screen.draw(x, y, '@');
}
}

void Screen::on()
{
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER);
surface = SDL_SetVideoMode(80, 160, 32, SDL_SWSURFACE | SDL_HWPALETTE);
font = SDL_LoadBMP(Font::Bitmap);
}

void Screen::off()
{
SDL_FreeSurface(font);
SDL_Quit();
}

void Screen::update()
{
SDL_Flip(surface);
}

void Screen::draw(int x, int y, char sym)
{
SDL_Rect src = {
(sym % Font::Columns) * Font::Width,
(sym / Font::Columns) * Font::Height,
Font::Width, Font::Height
};
SDL_Rect dest = {x * Font::Width, y * Font::Height, 0, 0};

SDL_BlitSurface(font, &src, surface, &dest);
}

void Screen::draw(const class Map& map)
{
for (int y = 0; y < Map::Height; ++y) {
for (int x = 0; x < Map::Width; ++x) {
draw(x, y, map.sectors[y * Map::Width + x]);
}
}
}

void Keyboard::on()
{
SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
}

void Keyboard::off() {}

Keyboard::Key Keyboard::getKey()
{
Screen.update();

SDL_Event event;
do { SDL_WaitEvent(&event); } while (event.type != SDL_KEYDOWN);

return static_cast<Keyboard::Key>(event.key.keysym.sym);
}

copx

unread,
Mar 2, 2011, 5:31:14 AM3/2/11
to

"Nik Coughlin" wrote in message
news:6f4d9aae-19ea-4641...@d23g2000prj.googlegroups.com...

>Also someone contributed a better Ruby version

The new Ruby version is beautiful, despite the uglyness
of curses. Unfortunately, it is also buggy as hell, because
of curses, unless the Ruby wrapper somehow changes how
curses works. Really, I am not making this up, Read The Fucking
Manual (the curses one in this case) if you don't believe me.
C+curses and Ruby+curses are buggy!
It seems you have to be a masocist to write correct curses
code. I mean, David Damerell used Perl, and that's hardcore
mascoism in my book, but he has been the only one thus
far to get this right.

Moral of the story: unless you are really in love with ancient
UNIX tech don't use it!

Nik Coughlin

unread,
Mar 2, 2011, 4:08:47 PM3/2/11
to
On Mar 2, 10:09 pm, "copx" <c...@gazeta.pl> wrote:
> "Nik Coughlin"  wrote in message
>
> news:6f4d9aae-19ea-4641...@d23g2000prj.googlegroups.com...
>
> >Would really like to see more languages, as always!
>
> Here is something actually useful. Please try to get
> Roguebasin to link to your project, because I don't
> want this code to rot unseen on some random corner
> of the internet.

It's a wiki so I guess anyone can add it. I'll look into it sometime.

> I implemented SimpleRL in C++ but
> that's not the point. I implemented it on top of SDL,
> demonstrating the "emulated ASCII" technique. So
> this should be useful for newbies who want to do
> that.

Thanks, added :)

David Damerell

unread,
Mar 3, 2011, 12:42:11 PM3/3/11
to
Quoting copx <co...@gazeta.pl>:
>"Nik Coughlin" wrote in message
>>Also someone contributed a better Ruby version
>of curses. Unfortunately, it is also buggy as hell, because
>of curses, unless the Ruby wrapper somehow changes how
>curses works. Really, I am not making this up, Read The Fucking
>Manual (the curses one in this case) if you don't believe me.
>C+curses and Ruby+curses are buggy!

What are the bugs which exist if you take into account the fact that getch
implies a refresh?
--
David Damerell <dame...@chiark.greenend.org.uk> Distortion Field!
Today is Mania, March.
Tomorrow will be Aponoia, March.

copx

unread,
Mar 3, 2011, 2:25:34 PM3/3/11
to

"David Damerell" wrote in message news:FPE*8s...@news.chiark.greenend.org.uk...

Quoting copx <co...@gazeta.pl>:
>"Nik Coughlin" wrote in message
>>Also someone contributed a better Ruby version
>of curses. Unfortunately, it is also buggy as hell, because
>of curses, unless the Ruby wrapper somehow changes how
>curses works. Really, I am not making this up, Read The Fucking
>Manual (the curses one in this case) if you don't believe me.
>C+curses and Ruby+curses are buggy!

>What are the bugs which exist if you take into account the fact that getch
>implies a refresh?

I explained that already in the post about the C version.

Curses manual (getch): "Depending on the setting of cbreak(), this is
after one character (cbreak mode), or after the first newline (nocbreak mode)."

Curses makes no guarantee about the state of the terminal, so you don't
know whether you are in cbreak() mode or not unless you explicitly activate
that mode. You did that, the others did not.

Nor did they clear the screen. The ruby guy makes it worse by addstr()ing
the map, assuming that he deals with a clear screen and that the cursor
is placed at 0,0. Curses does not guarantee this. In fact, even if he had
cleared the screen, the Ruby guy still needs to realize that curses clear() is
not guaranteed to move the cursor to 0,0 so he needs to do that too
before addstr()ing.:

Curses manual (clear): "This implementation, and others such as Solaris,
sets the current position to 0,0 after erasing via werase() and wclear().
That fact is not documented in other implementations, and may not be true of
implementations which were not derived from SVr4 source."

I also thought that not calling refresh() before getch() was a bug, but
I have to admit I missed the sentence about the implied refresh() in this case.
However, I am sure the people who wrote this code didn't know about
the implied refresh either. If they had read this part of the manual they
would have been aware of the need for cbreak(). I.e. the "It works on my system"
type of testing which will lead to ugly results if you use curses.
I am speaking from experience here.

There is another problem with clear() which I haven't mentioned yet. I know
from personal experience that you should not assume that clear() means
"make the entire screen black", which is a common assumption. You might
end up with a vterm background image shining through everywhere where
you didn't explicitly write spaces (happened to me).


David Damerell

unread,
Mar 3, 2011, 2:41:35 PM3/3/11
to
Quoting copx <co...@gazeta.pl>:
>"David Damerell" wrote in message news:FPE*8s...@news.chiark.greenend.org.uk...
>Quoting copx <co...@gazeta.pl>:
>>of curses. Unfortunately, it is also buggy as hell, because
>>of curses, unless the Ruby wrapper somehow changes how
>>curses works. Really, I am not making this up, Read The Fucking
>>Manual (the curses one in this case) if you don't believe me.
>>C+curses and Ruby+curses are buggy!
>>What are the bugs which exist if you take into account the fact that getch
>>implies a refresh?
>I explained that already in the post about the C version.
>Curses manual (getch): "Depending on the setting of cbreak(), this is
>after one character (cbreak mode), or after the first newline (nocbreak
>mode)."

Yes, that's a bug.

>Nor did they clear the screen.

The Ruby version calls init_screen which as I understand from the Ruby
docs does a clear().

>Curses manual (clear): "This implementation, and others such as Solaris,
>sets the current position to 0,0 after erasing via werase() and wclear().
>That fact is not documented in other implementations, and may not be true of
>implementations which were not derived from SVr4 source."

I would be amazed if it's not true of all modern implementations, and I'm
afraid (n)curses to a degree is in the situation where there's no formal
spec and the implementations define what the spec is. So I think that's
OK, although obviously I also agree that it's best to specify all
positions explicitly.

copx

unread,
Mar 3, 2011, 2:45:48 PM3/3/11
to

"copx" wrote in message news:ikopvr$mjm$1...@speranza.aioe.org...

>I also thought that not calling refresh() before getch() was a bug, but
>I have to admit I missed the sentence about the implied refresh() in this case.

..and you weren't aware of that either when you wrote your code!
Unless you deliberately put superfluous refresh() calls in your code
to make sure that curses doesn't get bored :P

This suggests: you read my posts, checked the man pages, discovered
this obviously not very well-known rule about the implied refresh,
and then posted this here in an attempt to appear more knowledgeable
than you actually are and to make me look bad. Pathetic.
Unfortunately, you forgot about your own code, which clearly
shows you weren't aware of this either.


copx

unread,
Mar 3, 2011, 3:08:42 PM3/3/11
to

"David Damerell" wrote in message news:--i*7U...@news.chiark.greenend.org.uk...

>The Ruby version calls init_screen which as I understand from the Ruby
>docs does a clear().

...which is correct. I was wrong about this, initscr() implies clear() as
the part of the first refresh() call. So this code works because initscr()
implies clear() before the first refresh while getch() implies a refresh()
if the screen has been changed since the last refresh() call. However,
somehow I don't believe the people who wrote the code in question
were THAT familiar with the finer details of curses (again - see the
lack of cbreak() calls). Also while the current man page says initscr()
implies clear() that certainly wasn't the case in the past. I used versions
of curses where initscr() did not imply clear(). That's why I thought
this was a bug.

copx

unread,
Mar 3, 2011, 3:59:52 PM3/3/11
to

"copx" wrote in message news:ikor5o$pcm$1...@speranza.aioe.org...

>..and you weren't aware of that either when you wrote your code!
>Unless you deliberately put superfluous refresh() calls in your code
>to make sure that curses doesn't get bored :P

I successfully kicked my internet forum habit not long ago. I no
longer enter long, pointless discussions about politics. Unfortunately,
it seems that I am still wasting time with stuff like this. Back in
my teenage years it didn't matter, time had no value. But nowadays
my time is precious. I have just wasted a lot of time reading curses
man pages and discussing these little pieces of demo code. Sorry folks,
r.g.r.development used to be one of my favorite places back then, but I
will always end up wasting my time if I keep posting here.
So I will try to make this the last post of my short "comeback".

However, the Ruby and C+curses guys really need to add cbreak()
to their code ;)

And if Mr. Damerell tries to deny, I ask you to look at his input/draw
loop:

while ($c = getch) {
if (defined($directions{$c})) {
$oldx=$player{'x'}; $oldy=$player{'y'};
$newx=$player{'x'}+$directions{$c}[0];
$newy=$player{'y'}+$directions{$c}[1];
if ($map[$newy][$newx] eq ' ') {
$oldx=$player{'x'}; $oldy=$player{'y'};
$player{'x'}=$newx; $player{'y'}=$newy;
&redraw ($oldx, $oldy); &redraw ($newx,$newy); refresh;
} else {
# you bumped into a wall
}
} elsif ($c eq 'r') {
&redraw_map;
} elsif ($c eq 'q') {
endwin; exit;
} else {
# complain, unknown keypress
}
}

I hope you will agree that the person who wrote that loop was not aware
of any implied refresh. He calls refresh() right before the loop repeats..
..starting with a getch() call. There is another superfluous refresh()
call but this one makes it particularly obvious.

Whatever, it doesn't really matter, and that's the point! I need to
go.

Happy coding folks! (that includes Mr. Damerell)

Björn Ritzl

unread,
Mar 4, 2011, 1:34:23 AM3/4/11
to
OMG!

/Björn

Konstantin Stupnik

unread,
Mar 4, 2011, 2:48:26 AM3/4/11
to
> OMG!
>
> /Björn

Closer to OMFG actually. :)

David Damerell

unread,
Mar 4, 2011, 11:52:17 AM3/4/11
to
Quoting copx <co...@gazeta.pl>:
>I hope you will agree that the person who wrote that loop was not aware
>of any implied refresh. He calls refresh() right before the loop repeats..
>..starting with a getch() call. There is another superfluous refresh()
>call but this one makes it particularly obvious.

Or it is possible I was copying bits of structure from my real Perl
roguelike project where the corresponding refresh; is necessary because
getch; is not necessarily coming next. Isn't it?
--
David Damerell <dame...@chiark.greenend.org.uk>
If we aren't perfectly synchronised this corncob will explode!
Today is Aponoia, March.
Tomorrow will be Epithumia, March - a weekend.

David Damerell

unread,
Mar 4, 2011, 11:54:18 AM3/4/11
to
Quoting copx <co...@gazeta.pl>:
>"David Damerell"
>>The Ruby version calls init_screen which as I understand from the Ruby
>>docs does a clear().
>...which is correct. I was wrong about this, initscr() implies clear() as
>the part of the first refresh() call. So this code works because initscr()
>implies clear() before the first refresh

No. This code works because Ruby's init_screen explicitly does a clear().

http://www.ruby-doc.org/stdlib-1.8.7/libdoc/curses/rdoc/classes/Curses.html#M000261

>lack of cbreak() calls). Also while the current man page says initscr()
>implies clear() that certainly wasn't the case in the past. I used versions
>of curses where initscr() did not imply clear(). That's why I thought
>this was a bug.

Whether or not initscr() implies clear() is totally irrelevant to the Ruby
version.

Ray

unread,
Mar 12, 2011, 11:47:36 AM3/12/11
to
copx wrote:


> It seems you have to be a masocist to write correct curses
> code. I mean, David Damerell used Perl, and that's hardcore
> mascoism in my book, but he has been the only one thus
> far to get this right.

Eh, curses isn't that bad. It does require some close
reading of the man pages though, and if you want well-
defined behavior you need to be pretty careful about
how you open it and set it up.

I'd been assuming these things were omitted because the
example is supposed to be simple and short, not because
people couldn't get it right if they wanted to.

Bear

Ray

unread,
Mar 12, 2011, 12:25:44 PM3/12/11
to

I almost forgot to say what I intended to say; I have found
using the Linux implementation of ncursesw, Which is
specifically the version that can handle Unicode characters,
to be unusually difficult to get right.

Short checklist:

1. User should use a UTF-8 locale.
2. User should use a Unicode-capable term.
3. User should use a console font that has glyphs for
the Unicode characters the program needs.

The above 3 things you the developer can't actually do much
about; that is the height of the bar for using a program that
deals with Unicode Text. Fortunately, nearly all users are
there by now.

4. Developer must use an ncurses configured to deal with
wide characters. This means it's based on ncurses 5.4 or
later, but *NOT* on version 11. So-called version 11 is
a pre-Unicode fork. Anyway, the version that can deal
with wide characters is called ncursesw.
5. There is a packaging mistake where the ncursesw docs are
not included in any ncursesw package. They are in the
ncurses package, with the version of ncurses INcompatible
with Unicode. Thus, you must install the wrong version
(ncurses) to get them and the right version (ncursesw)
to use them. In all, you must install ncurses, ncurses-dev,
ncursesw, and ncursesw-dev, and then be very careful to use
only the unicode-compatible libraries.
6. Code must call "setlocale" immediately after startup, before
starting curses or doing any other I/O, and set a UTF-8 locale.
7. Code must #define X_OPEN_SOURCE_EXTENDED in all source
files before all include statements. The wide-character
functions are part of the XOPEN standard, and if compiled
without this #define in effect they will not be visible
to your code. The libraries check for this #define to
see if you intend to use the XOPEN standard. The nasty
part of this is if you #define X_OPEN_SOURCE_EXTENDED only
where you think you need it (ie, for ncursesw itself) you
will get link failures because other standard libraries
will then be incompatible with the XOPEN version of ncursesw.
So, yes, you really have to #define this before all library
includes, in all source files, all the time. This requirement
is documented, but the full extent of it is not.
8. Code must include the right header file rather than the one
the documentation tells you to include. The man pages all
say #include curses.h, but what you actually must do is to
#include ncursesw/curses.h.
9. Developer must use the -lncursesw option while linking as
opposed to the -lncurses option.
10. Developer must NOT use -Wall or -Werror in the same compiler
invocation as -lncurses. If you do, the compiler will be
unable to find definitions for the wide-character functions.
This is completely undocumented, and bit me hard because
those are in my standard makefile compilation options. I
was starting to doubt the very existence of wide-character
functions before a simple test program where I called gcc
from the command-line without these options happened to
work.

Anyway, I have put this whole litany of ncursesw woes up on
roguebasin, so if you don't feel like copying the post to your
dev notes, you can just copy the URL instead.

http://roguebasin.roguelikedevelopment.org/index.php?title=Ncursesw

Bear

narf_the_mouse

unread,
May 8, 2011, 2:34:44 PM5/8/11
to
On 12/03/2011 9:25 AM, Ray wrote:
>
> I almost forgot to say what I intended to say; I have found
> using the Linux implementation of ncursesw, Which is
> specifically the version that can handle Unicode characters,
> to be unusually difficult to get right.
>
> Short checklist:
>
*Snip Litany of Woe*

>
> Anyway, I have put this whole litany of ncursesw woes up on
> roguebasin, so if you don't feel like copying the post to your
> dev notes, you can just copy the URL instead.
>
> http://roguebasin.roguelikedevelopment.org/index.php?title=Ncursesw
>
> Bear
>
>
>

All of that reads to me like very good reasons to use something else.

Ido Yehieli

unread,
Dec 2, 2011, 7:36:49 AM12/2/11
to
How can I commit my version?

It's c for DOS on the original IBM PC (8086 @ 4.77Mhz, 256kb ram, CGA graphics adapter).

-Ido.

Ido Yehieli

unread,
Dec 2, 2011, 7:53:40 AM12/2/11
to
Here is my version that works even on an 8086 :)

It compiles with the open watcom c compiler for 16-bit dos & runs fine on dosbox (and many versions of windows).

I only used conio.h for getch, if anyone knows how to do that with direct memory access this could be done without including conio.h:

srl.c
----
#include <string.h>
#include <conio.h>
#include <i86.h>

#define WIDTH 80
typedef struct {
int x;
int y;
} POINT;

/* io */
void clear() {
union REGS clear;
union REGS hide;

// clear screen
clear.w.cx = 0;
clear.w.dx = 0x1850;
clear.h.bh = 7;
clear.w.ax = 0x0600;
int86(0x10, &clear, &clear);

// hide cursor
hide.h.ah = 0x01;
hide.w.cx = 0x2607;
int86(0x10, &hide, &hide);
}
void draw_char(char ch, int x, int y, char color) {
int i;

i = 2 * (y * WIDTH + x);
((char far *) 0xb8000000)[i] = ch;
((char far *) 0xb8000000)[i+1] = color;
}
void draw_line(char * line, int x, int y, int w, char color) {
int ci = 0;
for (ci = 0; ci < w; ci++)
draw_char(line[ci], x + ci, y, color);
}
void draw_map(char * map, int x, int y, int w, char color) {
int h = strlen(map) / w;
int line;

for (line = 0; line < h; line++)
draw_line(map + line * w, 0, line, w, 1);
}
int get_key() {
return getch();
}

/* game logic */
void move(char * map, POINT * p, int x, int y, int w) {
draw_char(' ', p->x, p->y, 0);
if (map[y * w + x] == ' ') {
p->x = x;
p->y = y;
}
draw_char(' ', p->x, p->y, 15);
}

void main() {
char * map =
"#### ####"
"# #### #"
"# #"
"## ##"
" # # "
" # # "
"## ##"
"# #"
"# #### #"
"#### ####";
int map_w = 10;

POINT P = { 2, 2 };
char key = 0;

clear();
draw_map(map, 0, 0, map_w, 5);
draw_char(' ', P.x, P.y, 15);

while ((key = get_key()) != 27) {
switch (key) {
case 72:
move(map, &P, P.x, P.y - 1,map_w);
break;
case 80:
move(map, &P, P.x, P.y + 1,map_w);
break;
case 75:
move(map, &P, P.x - 1, P.y,map_w);
break;
case 77:
move(map, &P, P.x + 1, P.y,map_w);
break;
}
}
}
----


makefile
----
CC = wcc
CFLAGS = -zq -os -0 -ml
LINKER = wlink
LFLAGS = option quiet

OBJS = srl.obj

.c.obj : .autodepend
$(CC) $(CFLAGS) $<

game.exe : $(OBJS)
$(LINKER) $(LFLAGS) name $@ file { $< }
----

Ido Yehieli

unread,
Dec 2, 2011, 9:43:52 AM12/2/11
to
Removed conio, using only bios interrupts:

#include <string.h>
#include <i86.h>

#define WIDTH 80

void clear() {
union REGS clear;
union REGS hide;

// clear screen
clear.w.cx = 0;
clear.w.dx = 0x1850;
clear.h.bh = 7;
clear.w.ax = 0x0600;
int86(0x10, &clear, &clear);

// hide cursor
hide.h.ah = 0x01;
hide.w.cx = 0x2607;
int86(0x10, &hide, &hide);
}

void draw_char(char ch, int x, int y, char color) {
int i;

i = 2 * (y * WIDTH + x);
((char far *) 0xb8000000)[i] = ch;
((char far *) 0xb8000000)[i+1] = color;
}
void draw_line(char * line, int x, int y, int w, char color) {
int ci;
for (ci = 0; ci < w; ci++)
draw_char(line[ci], x + ci, y, color);
}
void draw_map(char * map, int x, int y, int w, char color) {
int h = strlen(map) / w;
int line;

for (line = 0; line < h; line++)
draw_line(map + line * w, 0, line, w, 1);
}

void get_key(unsigned char * scancode, unsigned char * ascii) {
union REGS getkey;
getkey.h.ah = 0;
int86(0x16, &getkey, &getkey);

*scancode = getkey.h.ah;
*ascii = getkey.h.al;
}

typedef struct {
int x;
int y;
} POINT;

void move(char * map, POINT * p, int x, int y, int w) {
if (map[y * w + x] == ' ') {
p->x = x;
p->y = y;
}
}

void main() {
char * map =
"#### ####"
"# #### #"
"# #"
"## ##"
" # # "
" # # "
"## ##"
"# #"
"# #### #"
"#### ####";
int map_w = 10;

POINT player = { 2, 2 };
unsigned char scancode = 0;
unsigned char ascii = 0;

clear();
draw_map(map, 0, 0, map_w, 5);

do {
draw_char(' ', player.x, player.y, 15);
switch (scancode) {
case 72:
move(map, &player, player.x, player.y - 1,map_w);
break;
case 80:
move(map, &player, player.x, player.y + 1,map_w);
break;
case 75:
move(map, &player, player.x - 1, player.y,map_w);
break;
case 77:
move(map, &player, player.x + 1, player.y,map_w);
break;
}
draw_char(' ', player.x, player.y, 15);

get_key(&scancode, &ascii);
}while (scancode != 1);
}

Ido Yehieli

unread,
Dec 2, 2011, 9:46:32 AM12/2/11
to
More readable version:

http://pastebin.com/MJ7u9ZDT

-Ido.
0 new messages