Blackjack is the longest game that I have converted thus far, and has several complex issues. The converted code spends most of the time in an unrunnable state until most everything is taken care of. Fortunately the systems and techniques built up over the previous conversions should hold me in good stead.

Regular expressions

Due to the excessive length of the code, regular expressions have become a vital tool to easily convert the vast majority of the code. Whenever I come across a part of a line that needs fixing or converting, regular expressions allow me to make that same fix to around 40 other lines throughout, that can all be fixed in the same way.

For example, BASIC statements that use conditional jumps to other lines, look like this:

IF Q>=11 THEN 590
IF X>1 THEN 570
...
IF Q<33 THEN 620

The following regular expression matches those statements, which can be replaced with other similar code:

find what: if (.+) then (\d+)
replace with: if ($1) {\n\tgoto $2\n}

Which results in the code being changed to:

if (Q>=11) {
    goto 590
}
if (IF X>1) {
    goto 570
}
...
if (Q<33) {
    goto 620
}

It’s closer to what we need, and other regular expressions help with a lot of the repetitive work, but I ‘m becoming more aware now that I need to have the computer do more of that repetitive work for me instead.

I will have to find easier ways to adjust the formatting of the code. Doing much of it by hand, even helped by the presence of regular expressions, is just too much like hard work.

Reviving the abandoned

The original code also included a function called fna that fna was defined but not used, instead opting to use three identical gosub statements. Translated code has been modified to use the fna function and remove those gosub statements.

10 DEF FNA(Q)=Q+11*(Q>=22)
...
3180 AA=Q(I): GOSUB 3400
3182 AB=Q(I+D1): GOSUB 3410
3184 AC=Q(D1): GOSUB 3420
...
3400 AA=AA+11*(AA>=22): RETURN
3410 AB=AB+11*(AB>=22): RETURN
3420 AC=AC+11*(AC>=22): RETURN

Instead of those separate gosub methods, the converted code actually uses the fna function to process the value.

function fna(q) {
    return q + 11 * negTest(q >= 22);
}
...
aa = fna(q_[i]);
ab = fna(q_[i + d1]);
ac = fna(q_[d1]);

A useful trick to simplify things involves goto statements that go to line numbers that are right beside each other.

3040 IF AA>16 THEN GOTO 3130
...
3110 IF Q<0 THEN GOTO 3140
...
3125 PRINT "---TOTAL IS";AA
3130 PRINT
3140 REM--TALLY THE RESULT

In many cases, line 3130 can be moved up, so that only the last of the goto lines is used as the target location.

function dealerHas() {
    ...
    if (aa > 16) { // 3040
        con.writeLine();
        return tallyResult();
    }
    ...
    if (q < 0) {
        return tallyResult();
    }
    ...
    con.writeLine("---TOTAL IS" + con.num(aa));
    con.writeLine();
    return tallyResult();
}
function tallyResult() {
    // tally the result // 3140
    ...
}

Nested Inputs

Another significant problem occurs when inputs are contained within loops. Because JavaScript does not stop when waiting for input, we need to continue on with the code after the input has been gained, and you cannot just jump in to the middle of a loop.

What you can do instead is to convert a loop in to a while statement, and then convert that in to some functions. Getting bets from each player, for example, the BASIC code is:

1880 PRINT "BETS:"
1890 FOR I=1 TO N: PRINT "#";I;: INPUT Z(I): NEXT I

With JavaScript, a first look at it is:

con.write("BETS:");
for (i = 1; i <= n; i += 1) {
    con.write("#" = i);
    state = states.GET_BET;
    con.readLine(commandDispatcher);
}

But that’s not all, because after getting the first input you cannot jump back in to the inside of a loop. So first we unroll that for loop into a while statement.

i = 1;
while (i <= n) {
    con.write("#" = i);
    state = states.GET_BET;
    con.readLine(commandDispatcher);
    i += 1; // still bad as we need to end execution after the readLine statement
}

This won’t do though, as we need to jump to that <code>i += 1</code> line after getting the input from the user. To achieve that we need to move the code inside of the while loop out to a separate function, and also the code below the readLine statement to a separate function too.

function initDeal() {
    ...
    i = 1;
    getBet();
}
function getBet() {
    if (i <= n) {
        con.write("#" = i);
        state = states.GET_BET;
        return con.readLine(commandDispatcher);
    }
    processBets();
}
function setBet(response) {
    z_[i] = response; // 1890
    i += 1;
    return getBet();
}
function processBets() {
    ...

That way when we stop at the readLine statement, we can carry on afterwards by jumping to the setBet() function, and then loop back to the top of the getBet() function for the next lot. It’s not pretty, but as I’m staying focused on keeping the structure as close as possible to the original, better web-based techniques will be held off until later.

You can give the Blackjack game a go at https://codepen.io/pmw57/pen/ZKbyyX

oznzl2g

Advertisements