Closed fwenzel closed 2 years ago
CC @lmorchard who is writing a pretty complex one in JS
Yes, this is a good discussion to have -- what are our available options?
We should pick a method and stick with it for all the JS implementations
I guess option 3 is command line scripting via node (example https://nodejs.org/api/readline.html#readline_rl_question_query_options_callback). Though I recognize that won't be vanilla JS, reading user input via node standard lib.
FWIW, this is how I've been doing it for Super Star Trek:
Kind of hacked together, but seems to be working decently so far
Stating the obvious, but if we settled on a common setup for this sort of thing, it wouldn't be too hard to establish a project-wide web-based runner
Personally, as someone who grew up copying BASIC programs out of magazines and is now a full-time javascript developer, I think the best bet would be option 1 from Jeff's list above. It's closest to the original experience of writing and using BASIC, AND easiest for a brand new user to access. Getting a new user to the point that they can take user input in a Node script is a very different thing (including importing standard library features, callbacks and/or promises, etc.) from using Window.prompt().
Also, the spirit (as I understand it) of this project is to re-implement the games in modern contexts. The examples I'm seeing so far of javascript ports with print(), tab(), input() functions look more like ports than re-implementations. I would think rewriting parts of the BASIC standard library in javascript so that the original source code requires less re-writing doesn't help new users of javascript learn how to think about programming. Am I thinking of this the wrong way?
Agree. It would be an entirely different, fun, thing to write a BASIC interpreter in all these languages so it can just run the original game....... but that's not what this repo is about. So a straight-up port of print()
to whatever language doesn't really teach anyone how to think about that language.
I am sure we will be doing a lot of refactoring along the way as we go.
It is a goal to use all the modern features of the languages to make the code more readable and easier to follow. We're not striving for cleverness, but rather clarity, and "teachability".
Is it good, clear code?
I would think rewriting parts of the BASIC standard library in javascript so that the original source code requires less re-writing doesn't help new users of javascript learn how to think about programming. Am I thinking of this the wrong way?
FWIW, the bits I did aren't a BASIC standard library so much as a very minimal interface to work with web vs CLI driver scripts
Also, the spirit (as I understand it) of this project is to re-implement the games in modern contexts.
Personally, I wouldn't want to re-implement everything from scratch. I might be a glutton for punishment, but I started tinkering with Super Star Trek as a literal conversion from the BASIC in order to make sure I at least understood the original.
From there, I've kept a few things basically the same - e.g. the original used string manipulation to maintain both the data model and presentation of the current sector in space. I thought it was interesting, so I kept that in JS rather than change to something else like an array.
But, there was a fairly complex tangle of code to calculate distance & direction that I couldn't quite make clean without GOTO. So, I just changed that to use Math.atan2
Might be nostalgia speaking, but I think it could be interesting to keep a mix of the original flavor with some modernizations.
@lmorchard I think that conversion to Math.atan2 is a great example. I think you made the right decision there. And like Jeff said, choosing simplicity over cleverness seems like the right approach. My main question was whether we were thinking of this as an exercise in porting or re-writing, the former being totally compatible with re-creating parts of BASIC in javascript, the latter leaning toward substituting modern approaches or the target platform's standard library (like Math in javascript).
Certainly not trying to be negative about anyone's work so far, but aiming for constructive criticism.
latter leaning toward substituting modern approaches or the target platform's standard library (like Math in javascript).
I think that's the general approach we want, to use the modern features of modern languages.
We definitely aren't looking to emulate BASIC.
I guess option 3 is command line scripting via node (example https://nodejs.org/api/readline.html#readline_rl_question_query_options_callback). Though I recognize that won't be vanilla JS, reading user input via node standard lib.
I like the idea of using NodeJS. At least should it not be excluded. Although a little more than just Window.prompt is required to read from stdin, its still very easy todo (see #171). The current browser based javascript implementations emulate a terminal prompt via html / css, which is far more verbose in my opinion, and misses the chance to reduce the code to its functional minimum. Maybe browser and nodejs based javascript implementations can co-exist, even for the same game :).
I think the project should reach the widest possible audience, so working directly in browser is the best solution yet. Also my current input/output solution works both in computers and cellphones. (see second rule: "...strive to replicate the command line / console output and behavior illustrated in the original book. ")
I'm having a crash with third rule "Please DO update for modern coding conventions. Support uppercase and lowercase. Use structured programming. Use subroutines. Try to be an example of good, modern coding practices!" because we need to be aware that the original programs weren't exactly an example of structured programming. Even for BASIC standards most programs are terribly ugly!
So far my current approach is this:
When the programs are ported, more people should make another pass that will take even more time:
Only the rule 2 from guidelines has prevented me from going text to full graphics XD
Gee... that remembers me that: "Any application that can be written in JavaScript, will eventually be written in JavaScript."
FWIW @nanochess With the print()
and input()
functions you’ve already built out (in at least the first three, I haven’t looked further), adding a window check should enable them to work with both browsers and Node.js
// Determine if code is running in browser;
// by default there is no window object in Node.js.
const isRunningInBrowser = typeof window !== 'undefined';
And then a simple if enables both browser and Node.js
function print(string) {
if (isRunningInBrowser) {
// Adds trailing newline to match console.log behavior
document
.getElementById('output')
.appendChild(document.createTextNode(string + '\n'));
} else {
console.log(string);
}
}
function input() {
if (isRunningInBrowser) {
// Accept input from the browser DOM input
return new Promise(function (resolve) {
let input_element = document.createElement('INPUT');
input_element.setAttribute('type', 'text');
input_element.setAttribute('length', '50');
document.getElementById('output').appendChild(input_element);
input_element.focus();
let input_str = undefined;
input_element.addEventListener('keydown', function (event) {
if (event.code === 'Enter') {
input_str = input_element.value;
document
.getElementById('output')
.removeChild(input_element);
print(input_str);
print('');
resolve(input_str);
}
});
});
} else {
// Accept input from the command line in Node.js
// See: https://nodejs.dev/learn/accept-input-from-the-command-line-in-nodejs
return new Promise(function (resolve) {
const readline = require('readline').createInterface({
input: process.stdin,
output: process.stdout,
});
readline.question('', function (input) {
resolve(input);
readline.close();
});
});
}
}
As console.log comes with newlines, I changed print()
to include newlines so the expected behavior is the same—but that means that to print without a newline requires another function
function printInline(string) {
if (isRunningInBrowser) {
document
.getElementById('output')
.appendChild(document.createTextNode(string));
} else {
process.stdout.write(string);
}
}
As I’m writing this I realize I could have just used process.stdout.write in print()
—but adding a newline character to every print statement is a bit of a slog.
I really like the style of the input/print functions I found in Acey Ducey. I rewrote them a bit to use modern JS idioms in https://github.com/coding-horror/basic-computer-games/pull/332, and factored them into their own module. But the overall style of preserving a "terminal-like" experience in the browser is very nice.
Something worth considering with modules is—as of current—if you try to load the HTML file locally (i.e. with a file:// URL), you'll run into CORS errors due to JavaScript module security requirements. You need to do your testing through a server.
From an instruction standpoint, the reveal of “just double-click the HTML file” seems quite appealing.
I also think the "javascript" implementation should target node (command line) as a default. It than would be quite easy to write a simple polyfill for input/output in the browser (I also like @LukasMurdock's aproach in https://github.com/coding-horror/basic-computer-games/issues/117#issuecomment-1003568796).
That would keep the code simpler (no DOM handling) and we could have one singel implementation for node and the browser.
Is anything in here helpful?
https://github.com/coding-horror/basic-computer-games/tree/main/00_Common
With #649 I've added a very basic example (without input) as an example on how this could look like. The javascript part is implemented as a node.js script. Together with a very simple polyfill for console.log
this also runs in the browser. I think with this approach we also could handle input.
There is also this
https://troypress.com/wp-content/uploads/user/js-basic/index.html
Just a side note as I haven't seen it in this discussion:
I absolutely love the Github Page which is generated: https://coding-horror.github.io/basic-computer-games/ . Even if we want a JavaScript (node) terminal version of the games, I would like to keep the Github pages version.
Yes, we have a decent solution now, closing this.
With #687 we move to node.js as main target! For playing games online we have a very simple node-terminal-emulator. Here is an example:
lacagy HTML Version: https://coding-horror.github.io/basic-computer-games/57_Literature_Quiz/javascript/litquiz.html
node.js version & terminal emuilator: https://coding-horror.github.io/basic-computer-games/00_Common/javascript/WebTerminal/terminal.html#57_Literature_Quiz/javascript/litquiz.mjs
This looks amazing ❤️
Small note : on my android phone the new version is not usable as it doesn't show a keyboard. The old version did
More a question than an Issue - do we have opinions on the rendering target (and template for it) of JavaScript implementations?
I saw a few solutions in here that appear to render text into the browser on a simple HTML page (example https://github.com/coding-horror/basic-computer-games/blob/main/73%20Reverse/javascript/reverse.js#L8 ).
Conversely the language selection process (https://discourse.codinghorror.com/t/updating-101-basic-computer-games-for-2021/7927/34) included the determination if a language is "scripting appropriate" by "running on the command line", which is true for JS, if you run it against
#!/usr/bin/env node
.So I am thinking either a) don't render it into the browser, but write ES6 for the command line OR b) we come up with some sort of template for how interacting with the browser should work.
Thoughts?