Input help?
Root / Programming Questions / [.]
CrazyblobCreated:
Btw if you want you can use DEF'd statements instead of GOTO because DEF is less likely to mess up somewhere and can be neater.
Lumage showed something using an IF statement and a GOTO. Not really a big deal I think.
The GOTOs were bugging me too .... a lot.
VAR RETRIES = 0, LOCKED = FALSE, PW$ VAR VALID_PASSWORD$[0] 'READ THE LIST OF VALID PASSWORDS READ PW$ WHILE PW$ != "" PUSH VALID_PASSWORD$, PW$ READ PW$ WEND PW$ = GET_PASSWORD$() IF PW$ == "V360" THEN PRINT "MASTER, .... IT'S YOU!" IF PW$ != "" THEN LOG_IN ELSE PRINT "GAME OVER" ENDIF END DEF GET_PASSWORD$() VAR PW$, I, VALID IF LOCKED THEN PRINT "ACCESS DENIED" ELSE VALID = FALSE REPEAT INPUT "PASSWORD"; PW$ 'IS THAT A VALID PASSWORD? FOR I = 0 TO LEN(VALID_PASSWORD$) - 1 IF PW$ == VALID_PASSWORD$[I] THEN VALID = TRUE BREAK ENDIF NEXT I IF VALID == FALSE THEN RETRIES = RETRIES + 1 PRINT "INVALID PASSWORD." IF RETRIES > 3 THEN LOCKED = TRUE PW$ = "" PRINT "TOO MANY FAILED LOGINS. LOCKED!" ENDIF ENDIF UNTIL VALID OR LOCKED ENDIF RETURN PW$ END DEF LOG_IN PRINT "C:>" END @PASSWORDS DATA "PASSWORD", "V360", ""Here is my overly long take with some tricky edge cases to puzzle over. Note that the password list is data driven. You can add, edit, and delete passwords without altering the code outside of the data statement. The pw$ variable in get_password and the one at global scope are different variables with the same name. Spooky. If you call get password after your retries are up it will kick you out. The while/wend, repeat/until and for loops save you from making a bunch of line labels as do the functions. You could move the code into new programs and reuse it now. Get_password has () in the call because it returns a value. Log_in doesn't return a value so you don't use ().
It was actually designed to be useful to someone just starting out and trying to learn.
GOTO has been considered harmful to writing clean code since the 70s. If you are just starting out using GOTO/GOSUB will hurt you in the long run and naturally lead to unmaintainable spaghetti code. In the example I showed WHILE/WEND, FOR/NEXT, and a REPEAT/UNTIL loops. I even put in an decent example of BREAK to skip unneccesary processing. I showed the different DEF syntax for a function and a subroutine too. Learning those will help you write cleaner, more maintainable, more reusable, more readable code.
There is a lot to study packed into less than 60 lines, but I do think it is worth studying.
I agree with Lumage. Throwing a huge pile of concepts at someone all at once, while telling them the stuff they do understand is bad and wrong, is not a good way to teach someone.
Yeah, GOTOs can contribute to hard-to-read code over time compared to more advanced control flow, but this is not an immediate problem that has to be addressed when they're still learning basic concepts like comparison operators. Program structure and code maintenance can be worried about later.
It also strikes me as odd that this is all in the name of cleaner and more readable code, because the code you've given is fairly convoluted and difficult to follow. GET_PASSWORD$() could be rewritten as something like this:
DEF CONTAINS(ARRAY[], ITEM) VAR I FOR I=0 TO LEN(ARRAY)-1 IF ARRAY[I]==ITEM THEN RETURN TRUE NEXT RETURN FALSE END DEF GET_PASSWORD$() VAR TRY,PW$ IF LOCKED THEN PRINT "ACCESS DENIED" RETURN "" ENDIF FOR TRY=1 TO 3 INPUT "PASSWORD";PW$ IF CONTAINS(VALID_PASSWORD$,PW$) THEN RETURN PW$ ELSE PRINT "INVALID PASSWORD." ENDIF NEXT PRINT "TOO MANY FAILED LOGINS. LOCKED!" LOCKED=TRUE RETURN "" END
My own take, while we're at it (to further modularize). Side note, this is purely functional with the exception of the CONTAINS$ function and PRINT statements.
DEF CONTAINS$(PASSWORDS$[], WORD$) VAR I FOR I=0 TO LEN(PASSWORDS)-1 IF PASSWORDS[I]==WORD THEN RETURN WORD NEXT RETURN "" END DEF CHECK$(WORD$) IF WORD$=="" THEN PRINT "Invalid password, try again!" ELSE PRINT "Success!" ENDIF RETURN WORD$ END DEF CHECKPASS$(CHECKS%) VAR PASS$ IF CHECKS%==3 THEN PRINT "Access denied, too many password attempts!" RETURN "" ELSE LINPUT "Input password: ", PASS$ VAR TESTER$=CHECK$(CONTAINS$(PASS)) IF TESTER$=="" THEN RETURN CHECKPASS$(CHECKS% + 1) ELSE RETURN TESTER$ ENDIF END CHECKPASS$(0)I haven't tested this, and I haven't written SB in over a year, so please let me know if there are any problems.