This is absolutely the correct thread! Programming is the main thing here, other stuff is secondary, I think.
As for splitting it up, the way that I'm doing it (and seen it done elsewhere) is, force yourself to split it up, by using abstract classes.
Create some "IO" abstract class (or interface or whatever your language has for this). That class will call your game logic, and your game will call that IO class. Figure out what functions need to be called. You only need one 'Game' so you don't need to make that abstract.
Your game will probably need to expose a "What character/foreground/background colors are in this tile?" and possibly "What image is in this tile?", so the IO subclasses can choose which ones to call. Any data that the IO is going to need, it needs to go through your Game class to obtain.
Also figure out how program flow is going to work. In my case, my IO class actually RUNS the program...it instantiates the Game class. I do it like that so that the IO has full control of the timer and stuff.
But in any case, for a pure roguelike, your Game is probably going to want to call functions on IO like:
- Create the window with optional height/width
- Redraw this tile (x,y)
- Redraw the whole screen
- Draw text on the header/footer/etc
- Delay for N milliseconds
- Get a key (waits for keypress, returns the key)
Keep in mind that for 'Get a key', you need to know what format to put it in. If you use SDL, there's like three different ways that it numbers all the keys on the keyboard. For a pure roguelike, it's probably quite enough to use a 'char' type. For other games you need a way to encode the user pressing and releasing 'shift' and other weird stuff, but do yourself a favor and don't bother with that or the mouse yet!
ANYWAY, once that's done. Your IO interface/abstract class/etc needs a pointer to your Game class, and your Game class needs a pointer to your IO. Now just make a class like "SDL_IO" or "Curses_IO" that implements "IO"...and if you do it the way I did, put your 'static void main()' in Curses_IO, and have that function instantiate a Curses_IO and a Game, give them each others' pointers. My game was kind of realtime and graphics, so I put my loop in my IO class. You probably want to put your loop in Game.
A common flow of operations looks like:
- I'm in the Game class, looping...
- Loop just started. Get a key from the user by calling io.GetKey()
- User pressed 'right arrow', which I've mapped to the char '6' for ease of use.
- There's no wall to the right, so move the user to the right!
- Tell the IO that it needs to redraw the player's old and new tiles.
- The IO says, "Okay, I'm drawing tile 6,6. I'll ask the game what tile is at 6,6."
- The game responds "That's the character '@'."
- The IO draws an @ at 6,6.
- The IO does the same thing and draws an '.' at 7,6.
- The game loops!
It seems like kind of an extra step to say "Draw the tile at this location" and making the IO say "Okay, let me ask what tile that is". But it makes a lot of sense because the game doesn't know if it wants graphics or ASCII, so IO needs to know what information to ask for. Also, when you redraw the whole screen all at once, IO is going to have to ask for what's in every tile...