Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JavaScript rendering target #117

Closed
fwenzel opened this issue Feb 28, 2021 · 25 comments
Closed

JavaScript rendering target #117

fwenzel opened this issue Feb 28, 2021 · 25 comments

Comments

@fwenzel
Copy link
Contributor

fwenzel commented Feb 28, 2021

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?

@fwenzel
Copy link
Contributor Author

fwenzel commented Feb 28, 2021

CC @lmorchard who is writing a pretty complex one in JS

@coding-horror
Copy link
Owner

Yes, this is a good discussion to have -- what are our available options?

  • render to the javascript console in the browser
  • render to a simple HTML target that emulates the console
  • (others?)

We should pick a method and stick with it for all the JS implementations

@fwenzel
Copy link
Contributor Author

fwenzel commented Feb 28, 2021

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.

@lmorchard
Copy link
Contributor

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

@lmorchard
Copy link
Contributor

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

@thomasqbrady
Copy link

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?

@fwenzel
Copy link
Contributor Author

fwenzel commented Feb 28, 2021

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.

@coding-horror
Copy link
Owner

coding-horror commented Feb 28, 2021

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?

@lmorchard
Copy link
Contributor

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.

@thomasqbrady
Copy link

@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.

@coding-horror
Copy link
Owner

coding-horror commented Mar 1, 2021

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.

@ideadapt
Copy link
Contributor

ideadapt commented Mar 2, 2021

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 :).

@nanochess
Copy link
Contributor

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:

  1. Port all programs, structuring them along the way as possible (some are pretty obscure! So this isn't so easy as it looks)
  2. Implemented one function for each GOSUB/RETURN subroutine. But only if possible, because some spaghetti code exits from the inside to another part on the code.
  3. Keep same variable names in order to correct and refer easily against the original. (read first rule: "...and compare implementations across common modern languages.")
  4. Keep comments from the original game.

When the programs are ported, more people should make another pass that will take even more time:

  1. Commenting the code properly. (per rule 3)
  2. Replacing single letter variables with more proper names. (per rule 4)
  3. Re-structuring the code (per rule 4)
  4. Supporting properly lowercase/uppercase.
  5. Declaring all variables properly (per rule 6).
  6. Separating the print/input functions in a separate library so the games can be executed with NodeJS.

Only the rule 2 from guidelines has prevented me from going text to full graphics XD

@NezumiRonin
Copy link
Contributor

NezumiRonin commented Mar 17, 2021

Gee... that remembers me that: "Any application that can be written in JavaScript, will eventually be written in JavaScript."

@LukasMurdock
Copy link
Contributor

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.

@domenic
Copy link
Contributor

domenic commented Jan 1, 2022

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 #332, and factored them into their own module. But the overall style of preserving a "terminal-like" experience in the browser is very nice.

@LukasMurdock
Copy link
Contributor

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.

@mojoaxel
Copy link
Collaborator

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 #117 (comment)).

That would keep the code simpler (no DOM handling) and we could have one singel implementation for node and the browser.

@coding-horror
Copy link
Owner

@mojoaxel
Copy link
Collaborator

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.

@coding-horror
Copy link
Owner

@MartinThoma
Copy link
Collaborator

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.

@coding-horror
Copy link
Owner

Yes, we have a decent solution now, closing this.

@mojoaxel
Copy link
Collaborator

mojoaxel commented Apr 2, 2022

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

@MartinThoma
Copy link
Collaborator

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests