Sorry your browser is not supported!

You are using an outdated browser that does not support modern web technologies, in order to use this site please update to a new browser.

Browsers supported include Chrome, FireFox, Safari, Opera, Internet Explorer 10+ or Microsoft Edge.

DarkBASIC Professional Discussion / Quiz Timer Help. Please HELP!

Author
Message
timer help
12
Years of Service
User Offline
Joined: 14th Jan 2014
Location:
Posted: 28th Jan 2014 19:04
Hello. I am having some trouble with a timer. The timer isn't working properly. Here is my whole code:

ShellfishGames
13
Years of Service
User Offline
Joined: 6th Feb 2013
Location:
Posted: 28th Jan 2014 23:31 Edited at: 28th Jan 2014 23:44
Not trying to be mean, but you can't really expect anybody to go through your almost 500 lines of code, not knowing where exactly your problem is and what might be wrong with it.

Try to implement your timer externally within the smallest possible context - a miniature program consisting of just the timer itself, for instance. If you get it to work, you can add it to your game. If it doesn't work, you can post the code here, tell us what exactly it's supposed to do (kind of important) and we will surely be able to help you out.

This "Something isn't working and I'll just leave you alone with all the code I have, hoping somebody will fix it" probably won't help.


Edit: Well, I checked your code anyway, it seems the timer itself is working - what isn't working however is the visualization (You could have written that in your initial post to make our lives easier, you know..). It's because you use Text 20,20,"Seconds Left: "+Str$(TimeLeft) continuously without clearing the screen in between, hence the numbers all overlap and after 2 seconds you just see a white box where the time should be. Usually you would add a cls to the beginning your question loops (the "repeat ... until TimeLeft = 0" loops (Note: I'd rather check for "TimeLeft <= 0", just in case the program hangs for some reason and skips a second), and then make sure your questions and possible answers are also inside the loop - but as this is not the case, and you'd have to change that thing at 10 places in the code, I'd suggest just doing the following:

Instead of this:



You just write this:



For all your questions. This added black box basically erases your previous timer output to make sure the next one will be readable. Rather bad solution, but it does the job I guess.

Phaelax
DBPro Master
23
Years of Service
User Offline
Joined: 16th Apr 2003
Location: Metropia
Posted: 29th Jan 2014 09:07 Edited at: 29th Jan 2014 09:07
Quote: " but you can't really expect anybody to go through your almost 500 lines of code"

Challenge accepted!


You're saying gosub in your program, but what you're actually doing is the same as using goto because you have no RETURN commands. Structuring your code is a lot more important at this point than worrying about the timer.

When the user presses 'X' to skip the instructions, the code moves on to ModeSelect.



The problem with that code is that the user will never have the chance to select any options. The reason is because of where you create the loops, INSIDE the inkey's statement.

It's late right now, but tomorrow I'll work on a tutorial that should hopefully help you out a lot.

ShellfishGames
13
Years of Service
User Offline
Joined: 6th Feb 2013
Location:
Posted: 29th Jan 2014 11:51 Edited at: 29th Jan 2014 11:52
Quote: "The problem with that code is that the user will never have the chance to select any options. The reason is because of where you create the loops, INSIDE the inkey's statement. "


Not sure if that's what you mean, but I believe the actual problem here is that the loop command belonging to the ModeSelect-loop is at the very bottom of the code instead of below the second if-block. Hence all questions are part of the mode select loop. Those loops within the if inkey$()-blocks are just there to wait until the user releases the key again.

Apart from that the Math subroutine doesn't even work, since it gosubs to Level12 (which is history) instead of Level1 (which is math).

@timer help: Where exactly did you learn programming? Is this quiz part of a school exercise or something? Or an online tutorial? Or a book? You really should start learning basics of how to structure your code (especially functions, possibly also file in/output) before working on programs that big. This isn't even much - a few hours playing around with functions and you're probably good to go. Let's see what Phaelax comes up with for you.

Phaelax
DBPro Master
23
Years of Service
User Offline
Joined: 16th Apr 2003
Location: Metropia
Posted: 29th Jan 2014 18:41 Edited at: 29th Jan 2014 18:57
I'm working on it right now.

As a side note, you need to proof read your code. Your 3rd math question is wrong. The expected answer is not correct and the correct value to the sum is not shown. Then on the first history question (about the presidents), choosing the correct answer still subtracts time instead of adding it like in the other questions.

Also, since you never change your font family or bold/italicize it, there's not need to set the font to arial more than once or call set text to normal all the time. For this example, changing between font sizes won't be an issue. However, it's worth noting that DB is very inefficient with using multiple font sizes. Jumping back and forth between different fonts and sizes can really slow down a program.

Phaelax
DBPro Master
23
Years of Service
User Offline
Joined: 16th Apr 2003
Location: Metropia
Posted: 29th Jan 2014 23:00 Edited at: 29th Jan 2014 23:13
I was going to make a pdf, but I think I'll just write it here for now.


Definitions:

Let's think about what defines a test question. You have the actual question itself, 4 available choices, and 1 correct answer. We can represent all of these variables in a single element. To do that, we define a UDT, or User-Defined Type.



What's you've created your UDT, you can then use it to set it as a variable's data type. Data types are integer, float, string, etc...

When you say x = 4, x is defined as an integer. If you want x to contain a decimal number, you need to make it a float by using the pound sign at the end of the variable like so: x# = 4.0
This is the same as if you explicitly defined the variables like this:
x as integer
x = 4


or

x as float
x = 4.0


Note that if you define the variable explicitly, you don't need the symbols like # (or $ for a string). So define a variable with a UDT would look like this:

test as TestQuestion

Now, the variable test can contain 6 different values, each with their own data type. And this is how you access those values:



All our test question will be structured into an array defined with our TestQuestion UDT.



Arrays:
An array is a list of items. What those items are depends on what data type you've defined for the array. In our case, it will be the UDT created above. Let's define an array of questions for a math test.



This defines math as an array with 5 elements of type TestQuestion. Instead of doing something like this:
math1 as TestQuestion
math2 as TestQuestion
math3 as TestQuestion

You create the array with the number of elements you want. The more you start using arrays, the more you'll see the incredible benefit and flexibility it gives you. What if you had 100 questions? You really want to define 100 different variables separately? It certainly would not be very convenient to access them. Everything would have to be hard-coded, much like how your levels are currently constructed.

To access the different elements in the array, you use an index number.


What can we do with this? It allows us to easily remove much of redundant code in your program. Look at each level in your project and you'll see a pattern. Each level has the same layout, the same text, the same controls. So why write it more than once? Again, what if you did this for 100 questions? What a nightmare! To simplify, we replace the hard-coded text with references from the array. Maintain a variable to track which question we're on, this will act as the index to the array. When you're ready to move onto the next question, increment that value.

Look over this code for displaying your test questions.


Do you see how by incrementing currentQuestion we can change the question on the screen without rewriting any code? Cool isn't it!

Footnote: When defining an array in DarkBasic, the index range is actually from 0 to size, thus the true size of your array is actually 1 more than the size you specified. You should not use that entire range as it would be bad practice since this is only present in DB and no other language. You should either use index values between 0 and size-1 or from 1 to size. Most languages start at 0 and go to size-1. It may be a good habit of getting used to that.


DATA command:
Above, I showed you how to access the values in a UDT and array so now you should be able to start writing and adding more test questions. However, there is another way of filling your array with questions and answers, and that is to use the DATA command.

This command allows you to create a series of data of any primitive type. Each chunk of data is separated by a comma or another data command. You can bunch it all up onto a single line, or put each chunk on a separate data command. It doesn't matter, so how you organize it should be based on what makes it easier for you to read and understand. You then access that data sequentially using the read command, which you use to assign those values to other variables. In this case we read the data into our array.



First, create a label to help identify what this data is for. The first data command I use to tell me how many questions will be read. Then each line contains 6 different values: the question, the 4 possible choices, and the answer. The answer is an integer number between 1 and 4 which will represent the user selecting A,B,C,or D.

Now look at the code to see how we read this data into the array using a FOR loop.


Whenever you call read, it'll read the next DATA value. An internal pointer keeps track of which data is next. To reset that pointer, we use a command called restore. Restore MathQuestion will put that pointer at the beginning of the MathQuestion label. Technically, since it's all sequential, you can leave out the restore HistoryQuestions in this example because the data falls immediately after the data for MathQuestion. However, it's good practice to use restore anyway, and it's safer.

We start by reading the first value, which is the number of questions. Then using that value we can define our array with the correct size. Now create the FOR loop to loop that same number of times as the size. Remember there are 6 values on each data line which represent a single question. So to read the entire line we call READ 6 times per loop for each question. READ take a single parameter, the variable it's going to read the data into. So for this example, it will be the various components of the UDT in each array element.



Main Loop:
Here's where we make the biggest change to your code's organization. The flow is much easier to understand and trace.




The series of IF statements you see will control which sub routine gets called. We need only to change the value of mode to jump to a new section.
Use these constants for better organization. Constants are like variables, except their values cannot be changed once set. And, as a general rule of thumb, constants are typically written in all uppercase.


The actual values can be anything you want, as long as they're different. But rather than having your code look like this:


You can use the constant names instead. The reason for this is because it's much easier to understand what each statement is doing. Plus, when you assign a value to mode, it's easier to remember which one does what. Does this enhance your program at all? No. But what it does is make life easier on the programmer.

Before, you had many loops to control key input. We're going to do away with all of that and use a flag instead. A flag has only two states, true or false; that is to say either the flag is raised up to signal something or the flag is down. Anytime the user presses a key, we raise the flag. We only accept user input when the flag is down. This prevents the same keystroke from being picked up by multiple inkey$() calls. The flag is lowered again once it's detected all keys have been released, as seen in this part of the main loop:

if inkey$() = "" then kFlag = 0


We start with mode defined as MODE_TITLE. This will then call the subroutine Title.


If the user presses h then change the mode so the instructions subroutine is called instead. Otherwise, the other key will simply set the mode to call the test subjection selection code.



The Test:
Here's were we finally get to put those arrays in action. But first, let's look at the beginning of the MathTest subroutine. If the test hasn't started yet, display the ready message to the user and await for keyboard input. Once the user has pressed the spacebar, startTest becomes true and we make a quick call to ResetTest.



What ResetTest does is reset a few important variables for us. These need to be reset any time a test is started or restarted. First, it starts us at question 1. Next, is the amount of time in the game. Then we grab the current time stamp which will be used for the countdown timer as seen in the main loop. And finally, make sure the test is not finished yet.



Now that the test has started, we move to the next section of code, which is contained inside the ELSE portion leftover from the snippet above.



First, we check if the test has finished. If not, then display the question with the possible choices. Now to get the user's selected answer.

answer = asc(lower$(inkey$())) - 96

Let's break this down. The user can select A, B, C, or D as the answer, but we marked the question answers with numbers 1-4. So how does this translate a letter into a number? First, get the user's input with inkey$(). Next, call lower$() as a safety measure so that both uppercase and lowercase letters can be accepted. ASC will return a character's ASCII value. A lowercase 'a' has a value of 97, b is 98, c is 99, and so on. Finally, we subtract 96 from that ascii value and we'll end up with 1 for A, 2 for B, and so on. Now all you have to do is check to see if the user selected the correct answer or not, but first make sure the user entered a valid selection.

if answer > 0 and answer < 5 and kFlag = 0

This will ensure that the answer is only checked if the user selected one of the 4 available choices and prevents any other keyboard input from triggering a response.

If the answer is correct, add more time. Otherwise, subtract from the remaining game time.


Move on to the next question by simply incrementing the value of currentQuestion. If the value becomes larger than the size of the array containing our questions, then we've run out of questions. Mark the test as finished (finishedTest = TRUE) and record the time to track how much time remained after completion.


The test is finished, show a stats/congratulatory page and wait for user input before moving on back to the main menu.




And here is the complete code. Comments are limited within the code because the documentation is all here in this thread explaining it all.


Login to post a reply

Server time is: 2026-07-07 00:09:35
Your offset time is: 2026-07-07 00:09:35