Reputation: 419
I am trying to convert some old qbasic (a old dos basic) code to python. I know python but not much qbasic (other than guessing the meaning of the syntax). This is the qbasic code
1020 DIM XS(499), A(504), V(99)
1560 GOSUB 2600 'Get coefficients
2600 REM Get coefficients
2660 CODE$ = "A"
2680 M% = 3
2690 FOR I% = 1 TO M% 'Construct CODE$
2700 GOSUB 2800 'Shuffle random numbers
2710 CODE$ = CODE$ + CHR$(65 + INT(25 * RAN))
2720 NEXT I%
2730 FOR I% = 1 TO M% 'Convert CODE$ to coefficient values
2740 A(I%) = (ASC(MID$(CODE$, I% + 1, 1)) - 77) / 10
2750 NEXT I%
2760 RETURN
2800 REM Shuffle random numbers
2810 IF V(0) = 0 THEN FOR J% = 0 TO 99: V(J%) = RND: NEXT J%
2820 J% = INT(100 * RAN)
2830 RAN = V(J%)
2840 V(J%) = RND
2850 RETURN
It appears it is mapping ASCII codes to random numbers but it is not clear to me how as I am not familiar with the syntax J% and V(J%), etc (dont know what the % means)
Upvotes: 2
Views: 1632
Reputation: 4689
This looks more like GW-BASIC than QBasic, although it's also valid QBasic. GW-BASIC variable names are all upper case, while QBasic isn't case sensitive but allowed lower case characters in names.
The postfix type characters where already mentioned in another answer and a comment to the question: %
denotes integer variables and $
is for strings. What wasn't mentioned is that A
, A%
, A$
and the arrays A()
, A%()
, A$()
are six different variables. So when translating code very closely to the original, one better encodes the type postfix into the name on the Python side of things. Unless you are really sure there are no names just differing by type. Given that most GW-BASIC programs used names of length one or two — to stay backwards compatible to older Microsoft BASIC dialects — ”collisions” in bigger programs where almost a given.
Python has no GOTO
/RETURN
, so one either has to inline those subroutines or define a function and declare the local scalar variables as global
.
XS()
isn't used and RAN
isn't initialised explicitly, but it has to be in Python. So a first translation might look like this:
from random import random
def shuffle_random_numbers():
global J_int, RAN
if V_array[0] == 0:
for J_int in range(100):
V_array[J_int] = random()
J_int = int(100 * RAN)
RAN, V_array[J_int] = V_array[J_int], random()
def get_coefficients():
global CODE_str, M_int, I_int, RAN
#
# Generate a four letter string starting with "A" followed by three random
# characters from the range A..Y (inclusive).
#
CODE_str = "A"
M_int = 3
for I_int in range(1, M_int + 1):
shuffle_random_numbers()
CODE_str += chr(65 + int(25 * RAN))
#
# Convert the letters into values between -1.2 and 1.2.
#
# "A" = -1.2, "B" = -1.1, "C" = -1.0, "D" = -0.9, …, "X": 1.1, "Y" = 1.2
#
for I_int in range(1, M_int + 1):
A_array[I_int] = (ord(CODE_str[M_int]) - 77) / 10
RAN = 0 # Initialised implicitly in the BASIC code.
A_array = [0] * 505
V_array = [0] * 100
get_coefficients()
shuffle_random_numbers()
looks bogus. Either it seems really unnecessary as it just stores random()
/RND
results and uses the last result to pick one of the stored numbers and replaces it by a new random number. Like random()
/RND
it returns random numbers between 0 and 1; just in a different order. If this is because the original programmer felt the need to make the random numbers ”more random”, then scrap that function and just use random()
.
If that particular order is relevant to the program, you can't just use Python's random()
but need to recreate the original pseudo random number generator of the BASIC dialect used to run the original program. If it's GW-BASIC then there is a faithful open source implementation written in Python: PC-BASIC.
For get_coefficients()
, assuming M%
and I%
are just used locally, and using arguments and a return value we might end up here:
import random
from string import ascii_uppercase
A_TO_Y = ascii_uppercase[:-1]
assert A_TO_Y[-1] == "Y"
def set_coefficients(values):
code = "A" + "".join(random.choice(A_TO_Y) for _ in range(3))
#
# Convert the letters into values between -1.2 and 1.2.
#
# "A" = -1.2, "B" = -1.1, "C" = -1.0, "D" = -0.9, …, "X": 1.1, "Y" = 1.2
#
offset = ord(A_TO_Y[0]) + len(A_TO_Y) // 2
for i, letter in enumerate(code, 1):
values[i] = (ord(letter) - offset) / 10
return code
def main():
values = [0] * 505
code = set_coefficients(values)
if __name__ == "__main__":
main()
Notice the change in the function name, because no reader would expect a get_*()
function to actually set values in the sequence given as argument.
Upvotes: 1
Reputation: 3058
As mentioned, in QBasic the %
, $
and #
denote the datatype of the variable. In Python you don't have to specify the datatypes of variables, but in QBasic it's like this:
QBasic
I% ' % = integer variable
str$ ' $ = string variable
f# ' # = floating point variable
And if you want to convert QBasic functions, see these questions: Python equivalent of mid and Python get ASCII value
QBasic Python
str$ = "water" str = "water"
ASC("A") ord("A") // result 65
MID$(str$, 3, 2) str[3,(3+2)] // result "te"
CHR$(65) chr(65) // result "A"
Upvotes: 2