AI Computer Institute
Expert-curated CS & AI curriculum aligned to CBSE standards. A bharath.ai initiative. About Us

Grade 8 AI & Computer Science Practice Questions — Set 10

20 questions from the Grade 8 bank, each with its answer and a full explanation. Set 10 of 11 · 221 questions in this grade.

Reading is revision; testing is practice. Take the same questions as a timed quiz →

Question 181 · CSS Grid · hard

A web developer is building the mobile layout for a train-ticket booking app (in the style of IRCTC) using CSS Grid: ```css .container { display: grid; grid-template-columns: 60px 1fr 3fr; gap: 20px; width: 580px; } ``` The container has no padding or border, and it holds exactly three columns: a fixed 60px icon strip, then the 1fr and 3fr tracks. What is the rendered width, in pixels, of the second column (the one sized with `1fr`)?

  1. 120px
  2. 130px
  3. 135px
  4. 160px

Answer: A. 120px

ExplanationThe browser lays out a grid in a fixed order: first it hands out space to fixed-size tracks, then to gaps, and only what's left over gets divided among the flexible `fr` tracks in proportion to their fr values. Here the container is 580px wide with three columns, so there are two gaps between them: 2 × 20px = 40px reserved for gaps. The fixed track takes 60px. That leaves 580 − 60 − 40 = 480px to be shared between the `1fr` and `3fr` tracks. Those two tracks together represent 1 + 3 = 4 fr "shares," so each share is worth 480 ÷ 4 = 120px. The `1fr` column therefore renders at 1 × 120px = 120px (and the `3fr` gallery column takes the remaining 3 × 120px = 360px). Adding everything back up confirms it: 60px + 120px + 360px + 40px of gaps = 580px, exactly matching the container. 130px comes from forgetting to remove the gap space before dividing (580 − 60 = 520, then 520 ÷ 4 = 130). 135px comes from the opposite slip — forgetting to remove the fixed 60px track (580 − 40 = 540, then 540 ÷ 4 = 135). 160px comes from dividing the correct 480px leftover by the number of columns (3) instead of the number of fr shares (4). Each of these skips one step of the two-stage allocation that `fr` units actually use: fixed and gap space is committed first, and only the remainder is split proportionally among the fr tracks.

Question 182 · Memory Management: How Computers Remember · hard

A computer program stores the marks of 40 students in 5 subjects using a two-dimensional integer array marks[40][5], laid out in memory in row-major order (row by row) starting at base address 2000. Each integer occupies 4 bytes. Using 0-based indexing, what is the memory address of marks[12][3] — the 13th student's 4th-subject mark?

  1. 2252
  2. 2063
  3. 2272
  4. 2528

Answer: A. 2252

ExplanationIn row-major order, the computer lays every row of the array end-to-end in memory before starting the next row, so reaching marks[12][3] means first skipping 12 complete rows of 5 marks each, then 3 more marks within row 12: (12 × 5) + 3 = 63 marks come before it. Since each mark is a 4-byte integer, those 63 skipped marks occupy 63 × 4 = 252 bytes. Adding that offset to the base address gives 2000 + 252 = 2252, the correct memory address. 2063 comes from computing the right element count (63) but forgetting that each element takes 4 bytes of memory — adding the raw count of skipped elements directly to the base address instead of converting it to bytes first. 2272 comes from an off-by-one indexing slip: treating "the 13th student" as row index 13 instead of remembering that 0-based indexing makes the 13th student row 12. Using row 13 gives (13 × 5 + 3) × 4 = 272, and 2000 + 272 = 2272. 2528 comes from mixing up row-major and column-major layout — computing the offset as if columns were stored first, using (column × number of rows + row) instead of (row × number of columns + column): (3 × 40 + 12) × 4 = 528, giving 2000 + 528 = 2528. This is the address the element would have under column-major storage, not the row-major layout the array actually uses.

Question 183 · Games with Pygame: Building Interactive Programs · hard

A Grade 8 student is building a Pygame endless-runner where a player rectangle chases a stationary enemy rectangle. The game loop runs at a fixed 60 FPS, and the player moves right by 5 pixels every frame: ```python player = pygame.Rect(100, 200, 50, 50) # x, y, width, height enemy = pygame.Rect(250, 180, 40, 40) speed = 5 fps = 60 frame = 0 while not player.colliderect(enemy): player.x += speed frame += 1 print(frame / fps) ``` Given that Pygame's colliderect() only reports a collision when the rectangles genuinely overlap — two rectangles that merely touch at an edge are NOT considered colliding — what value does this program print, to two decimal places?

  1. 0.35 seconds, because colliderect() only turns True once the player's right edge (x + 50) strictly passes the enemy's left edge at 250, which first happens at x = 205 after 21 frames
  2. 0.33 seconds, because the rectangles are already treated as colliding as soon as the player's right edge just reaches the enemy's left edge at x = 200
  3. 0.50 seconds, because the collision is judged by comparing the player's raw x-position to the enemy's x-position directly, so contact only occurs once x reaches 250 after 30 frames
  4. 0.38 seconds, because the frame counter increments one extra time after colliderect() first returns True before the loop actually exits

Answer: A. 0.35 seconds, because colliderect() only turns True once the player's right edge (x + 50) strictly passes the enemy's left edge at 250, which first happens at x = 205 after 21 frames

ExplanationPygame's colliderect() checks real overlap, not mere contact: for two rects it requires enemy.x < player.x + player.width AND player.x < enemy.x + enemy.width (plus the matching pair on the y-axis) — all with strict inequalities, so edges that only touch do not count as colliding. Here the player's y-range (200 to 250) already overlaps the enemy's y-range (180 to 220) on every single frame, so the outcome is decided entirely by x. The player starts at x = 100 and gains 5 pixels each frame, so after k frames its position is x = 100 + 5k. The overlap requirement 250 < x + 50 simplifies to x > 200, and since x only ever lands on multiples of 5 above 100, the first value that actually clears 200 is x = 205 — not x = 200, which merely touches the enemy's edge and still fails the strict inequality. Reaching x = 205 takes k = 21 frames (100 + 5 x 21 = 205). At 60 FPS, 21 frames correspond to 21 / 60 = 0.35 seconds, which is exactly what print(frame / fps) outputs. A student who accepts x = 200 as "touching = colliding" stops one frame early and gets 0.33 seconds, while a student who forgets to add the player's own 50-pixel width before comparing to the enemy's edge waits until x = 250, overshooting to 0.50 seconds — both errors come from misreading how colliderect()'s strict overlap test actually works.

Question 184 · Probability Fundamentals · hard

A student builds a two-dice game simulator for her school coding-club fest using two independent calls to a random-integer generator, each equally likely to return 1 through 6: ```python die1 = random_int(1, 6) die2 = random_int(1, 6) ``` For the app's documentation she needs a conditional probability: given that at least one of the two calls returned 5, what is the probability that die1 + die2 equals 8?

  1. 5/36
  2. 1/6
  3. 2/11
  4. 1/11

Answer: C. 2/11

ExplanationSince die1 and die2 are two separate calls to the generator, treat every ordered pair (die1, die2) as a distinct, equally likely outcome — there are 6 x 6 = 36 such pairs in total, and (5,3) is a different outcome from (3,5) even though both sum to 8. The condition "at least one call returned 5" shrinks the sample space before you even look at the sum. List every pair containing a 5: with die1 = 5, die2 can be 1, 2, 3, 4, 5, or 6 (6 pairs); with die2 = 5, die1 can be 1, 2, 3, 4, or 6 (5 more pairs, since (5,5) was already counted). That's 11 equally likely outcomes forming the new, restricted sample space. Now check which of those 11 pairs sum to 8: only (5,3) and (3,5) qualify — (5,5) sums to 10, (5,1) to 6, (5,2) to 7, (5,4) to 9, (5,6) to 11, and their mirror images give the same sums. So 2 of the 11 restricted outcomes are favorable, giving a conditional probability of 2/11. 5/36 is the *unconditional* probability of rolling a sum of 8 (5 favorable pairs out of the original 36) — it ignores that the sample space was already narrowed down by the "at least one 5" condition, so the denominator is wrong. 1/6 comes from a subtly different question: if you already knew one *specific* die (say die1) was fixed at 5, the other die would need to show 3, giving 1/6 — but "at least one die shows 5" doesn't tell you which die it is, so this undercounts the true restricted sample space (11 outcomes, not 6). 1/11 comes from correctly finding the 11-outcome restricted sample space but then treating (5,3) and (3,5) as the same outcome and counting only 1 favorable case, which double-loses information since the two random_int() calls are independent and order matters.

Question 185 · Binary and Hexadecimal Number Systems · hard

A low-cost 8-bit microcontroller inside a FASTag toll-plaza reader stores two intermediate toll amounts as unsigned 8-bit binary numbers, 11011010 and 10110111 (in internal units of ₹1 each). Its arithmetic unit adds them directly in an 8-bit register, so if the true sum needs a 9th bit, that carry simply falls off the register and is lost — a classic fixed-width overflow. What hexadecimal value is actually left sitting in the register after this addition?

  1. 0x191, since the addition genuinely produces a 9-bit result and the full value, carry bit included, is what the register ends up holding.
  2. 0x91, since only the register's low 8 bits survive — the carry generated out of bit 7 is discarded, leaving 145 in decimal.
  3. 0x6D, since adding the two binary numbers bit-by-bit while ignoring every carry is the same as computing their bitwise XOR.
  4. 0x19, since the register overflow is resolved by dropping the extra hexadecimal digit from the end of the number that holds the smaller place values.

Answer: B. 0x91, since only the register's low 8 bits survive — the carry generated out of bit 7 is discarded, leaving 145 in decimal.

ExplanationConverting each operand to decimal first: 11011010 = 128+64+16+8+2 = 218, and 10110111 = 128+32+16+4+2+1 = 183. Their true sum is 218+183 = 401, which needs 9 bits (110010001 in binary) because it exceeds 255, the largest value an 8-bit register can hold. Since the register is only 8 bits wide, the carry out of bit 7 is not stored anywhere — it simply vanishes, exactly the way real fixed-width hardware registers overflow. What remains is 401 mod 256 = 145, i.e. the low 8 bits 10010001. Splitting 10010001 into two nibbles, 1001 and 0001, gives the hex digits 9 and 1, so the register's final content is 0x91. Discarding the carry from the top of the number, the way overflow always works, is different from lopping a digit off the bottom, and it is also not the same operation as an XOR, which throws away every carry generated during the addition rather than just the one carry that overflows the register's width.

Question 186 · Building Chatbots · hard

A student builds a simple rule-based chatbot in Python. Each intent has a set of keywords, and the chatbot picks whichever intent has the highest confidence score, defined as (number of matched keywords) ÷ (total keywords in that intent's set) — not the raw number of matches. Here is the code: ```python intents = { "greeting": {"hello", "hi"}, "weather": {"weather", "rain", "temperature", "forecast", "climate"}, "book_ticket": {"book", "ticket", "train", "irctc", "pnr"} } def get_response(message): words = set(message.lower().split()) best_intent = None best_confidence = 0 for intent, keywords in intents.items(): matches = len(words & keywords) confidence = matches / len(keywords) if confidence > best_confidence: best_confidence = confidence best_intent = intent if best_confidence < 0.3: return "sorry" return best_intent ``` What does `get_response("hi can you tell me the weather forecast for tomorrow")` return?

  1. "greeting" — because its confidence score (1 matched keyword out of 2) is 0.5, which is higher than weather's 0.4, even though weather matched more keywords overall.
  2. "weather" — because it matched two keywords ("weather" and "forecast"), more than any other intent, and the function returns whichever intent has the highest raw match count.
  3. "book_ticket" — because the message refers to travel context like "tomorrow", which the function treats as an implicit signal to start the ticket-booking flow.
  4. "sorry" — because none of the three intents match every word in the message, so no confidence score is ever allowed to cross the 0.3 threshold.

Answer: A. "greeting" — because its confidence score (1 matched keyword out of 2) is 0.5, which is higher than weather's 0.4, even though weather matched more keywords overall.

ExplanationThe function scores each intent as (matched keywords) ÷ (total keywords defined for that intent) — a normalized confidence, not a raw match count. For the message "hi can you tell me the weather forecast for tomorrow", the unique word set is {hi, can, you, tell, me, the, weather, forecast, for, tomorrow}. greeting's keyword set is {"hello", "hi"} (2 keywords); only "hi" matches, so confidence = 1/2 = 0.5. weather's keyword set is {"weather", "rain", "temperature", "forecast", "climate"} (5 keywords); two words match ("weather" and "forecast"), so confidence = 2/5 = 0.4. book_ticket's keywords {"book", "ticket", "train", "irctc", "pnr"} never appear in the message at all, so its confidence is 0 — the word "tomorrow" is not one of its keywords and the code has no logic that treats travel-sounding words as booking signals. Walking through the loop in insertion order: greeting sets best_confidence to 0.5, then weather's 0.4 fails to beat it, then book_ticket's 0 also fails, so best_intent stays "greeting". Since 0.5 is not less than the 0.3 threshold, the "sorry" fallback never fires, and the function returns "greeting". This is a genuine trap in keyword-based chatbot design: an intent with fewer, more specific keywords can outrank an intent with more raw matches once the score is normalized by keyword-set size — which is exactly why real systems, including an IRCTC-style ticket-booking bot, use a normalized confidence rather than a plain match count to decide which intent to trust.

Question 187 · API Authentication · hard

A student building a UPI-linked expense tracker sends this HTTP header to authenticate with a bank's practice-sandbox API over a plain (non-HTTPS) connection: Authorization: Basic YWRtaW46UGFzc3cwcmQh. This is HTTP Basic Authentication, where the value after "Basic" is the Base64 encoding of username:password. What is the most accurate statement about the security of this request?

  1. Because Base64 is a reversible encoding with no secret key involved, anyone who intercepts this header can immediately decode it back to the plaintext credentials admin:Passw0rd!, so Basic Auth is only safe when sent over HTTPS.
  2. The string is a one-way cryptographic hash of the credentials, so even if a network sniffer captures this header, the original username and password can never be reconstructed from it.
  3. Base64 encoding here functions as encryption keyed to the bank's server certificate, so the credentials stay hidden from anyone who intercepts the header over plain HTTP.
  4. This header proves the developer's identity was already verified by a trusted authorization server, exactly like the access token issued at the end of an OAuth 2.0 flow.

Answer: A. Because Base64 is a reversible encoding with no secret key involved, anyone who intercepts this header can immediately decode it back to the plaintext credentials admin:Passw0rd!, so Basic Auth is only safe when sent over HTTPS.

ExplanationThe value after "Basic" in an HTTP Basic Authentication header is not encrypted or hashed — it is Base64, a reversible text encoding with no secret key. Decoding YWRtaW46UGFzc3cwcmQh gives back exactly admin:Passw0rd!, the original username:password pair, using nothing more than a public, standard decoding table that any programming language can run in one line of code. That is precisely why HTTP Basic Authentication must always travel over HTTPS/TLS: TLS encrypts the entire request, including this header, so an eavesdropper only ever sees ciphertext. Sent over plain HTTP as in this scenario, the credentials are exposed the instant the request is captured — reversing Base64 takes no computing power and no key at all. It also is not a cryptographic hash, since hashes are one-way by design and cannot be decoded back into their original input the way Base64 can. And it has nothing to do with OAuth 2.0's delegated-trust model, where a separate authorization server verifies identity once and issues a short-lived access token, rather than the client resending raw, reversible credentials on every single request.

Question 188 · SQLite with Python: Your Portable Database · hard

A transport-analytics student in Bengaluru runs the following Python program against an in-memory SQLite database of train delay records: ```python import sqlite3 conn = sqlite3.connect(":memory:") cur = conn.cursor() cur.execute("CREATE TABLE trains (name TEXT, city TEXT, delay INTEGER)") data = [ ("Rajdhani Express", "Delhi", 15), ("Shatabdi Express", "Mumbai", 0), ("Duronto Express", "Delhi", 30), ("Garib Rath", "Mumbai", 10), ("Vande Bharat", "Delhi", 0), ("Tejas Express", "Chennai", 5), ] cur.executemany("INSERT INTO trains VALUES (?, ?, ?)", data) cur.execute(""" SELECT city, COUNT(*), AVG(delay) FROM trains GROUP BY city HAVING COUNT(*) >= 2 ORDER BY AVG(delay) DESC """) ``` After this code finishes executing, what does `cur.fetchall()` return?

  1. Three tuples appear because the HAVING clause is evaluated before GROUP BY collapses the rows, so it cannot filter out any city: [('Delhi', 3, 15.0), ('Mumbai', 2, 5.0), ('Chennai', 1, 5.0)]
  2. Each surviving city appears, but count and average are swapped inside the tuple, matching the column order in the CREATE TABLE statement instead of the SELECT clause: [('Delhi', 15.0, 3), ('Mumbai', 5.0, 2)]
  3. Two tuples appear, in this exact order: [('Delhi', 3, 15.0), ('Mumbai', 2, 5.0)]
  4. The same two tuples as the correctly grouped result, but in reverse order, because ORDER BY ... DESC is assumed to sort groups alphabetically by the GROUP BY column rather than by the aggregate: [('Mumbai', 2, 5.0), ('Delhi', 3, 15.0)]

Answer: C. Two tuples appear, in this exact order: [('Delhi', 3, 15.0), ('Mumbai', 2, 5.0)]

ExplanationGrouping the six rows by city produces three buckets: Delhi has three rows with delays 15, 30, and 0, giving count 3 and average 45/3 = 15.0; Mumbai has two rows with delays 0 and 10, giving count 2 and average 10/2 = 5.0; Chennai has a single row with delay 5, giving count 1 and average 5.0. HAVING runs after grouping, not before, so HAVING COUNT(*) >= 2 checks each group's row count and drops Chennai's single-row group while keeping Delhi and Mumbai. The SELECT clause lists city, COUNT(*), then AVG(delay) in that order, so each surviving row is a tuple with city first, the integer count second, and the float average third — never swapped. ORDER BY AVG(delay) DESC then sorts the surviving groups by their average delay from highest to lowest, not alphabetically by city: Delhi's average of 15.0 is higher than Mumbai's 5.0, so Delhi's tuple is listed first. Since sqlite3's fetchall() returns query results as a list of tuples, the final result is [('Delhi', 3, 15.0), ('Mumbai', 2, 5.0)].

Question 189 · Debugging Techniques · hard

Priya is building a Python function for a CBSE report-card app that averages Arjun's 5 unit-test scores (each out of 100). The average it prints looks wrong, so she adds a debug `print()` inside the loop to trace exactly which marks get summed: ```python def average(marks): total = 0 for i in range(1, len(marks)): total += marks[i] print("DEBUG: added marks[" + str(i) + "] =", marks[i]) return total / len(marks) marks = [78, 85, 92, 66, 90] print("Average:", average(marks)) ``` Running it produces this trace: ``` DEBUG: added marks[1] = 85 DEBUG: added marks[2] = 92 DEBUG: added marks[3] = 66 DEBUG: added marks[4] = 90 Average: 66.6 ``` Based on what this debug trace reveals, what is the actual bug, and what should the average be once it is fixed correctly?

  1. The loop's `range(1, len(marks))` starts at index 1 instead of 0, so `marks[0]` (78) is never added — the trace confirms this, since no line ever prints "added marks[0]". The total should be 411, not 333, so changing the range to start at 0 fixes the average to 82.2.
  2. The `total` variable is not reset to 0 before the loop begins, so leftover values from an earlier call are carrying into this sum; explicitly adding `total = 0` right before the `for` loop would correct the average to 82.2.
  3. The division `total / len(marks)` is the bug, since the loop only ever sums 4 values, not 5; changing it to `total / (len(marks) - 1)` correctly fixes the average to 83.25 without needing to touch the loop.
  4. The `print()` statement inside the loop is itself altering the value of `total` as the program runs, so simply deleting the DEBUG print line restores the correct sum and produces an average of 82.2.

Answer: A. The loop's `range(1, len(marks))` starts at index 1 instead of 0, so `marks[0]` (78) is never added — the trace confirms this, since no line ever prints "added marks[0]". The total should be 411, not 333, so changing the range to start at 0 fixes the average to 82.2.

ExplanationThe trace is the key debugging clue here: it prints exactly 4 "added marks[...]" lines, for indices 1 through 4, and index 0 never appears. That pins the bug precisely to `range(1, len(marks))` — it should be `range(len(marks))` (equivalently `range(0, len(marks))`) so the loop starts at index 0 and includes `marks[0]`, which is 78. Adding up all five scores correctly gives 78 + 85 + 92 + 66 + 90 = 411, and dividing by the full count of 5 scores gives 411 / 5 = 82.2 — the true average. The buggy version only summed 85 + 92 + 66 + 90 = 333 and divided by 5, giving the incorrect 66.6 shown in the trace. This illustrates why tracing intermediate values (not just the final output) is such a powerful debugging technique: the missing "marks[0]" line in the output immediately localizes the bug to the loop's starting bound, rather than leaving you guessing between the loop, the sum, or the division. The other explanations don't survive a check against the code or the trace: `total = 0` is already present before the loop, so it isn't left over from a previous call; patching the divisor to `len(marks) - 1` treats a loop bug as if it were a division bug, and only coincidentally lands near the right ballpark (333/4 = 83.25, still wrong); and a `print()` statement never modifies a program's variables — it only displays them, so removing it cannot change what `total` evaluates to.

Question 190 · Agile Methodology: How Real Teams Build Software · hard

A Scrum team at a Bengaluru fintech startup is building a new UPI payment feature. Across three completed two-week sprints, they delivered 20, 25, and 18 story points. The remaining product backlog has 130 story points of work. If the team forecasts using their average velocity, and a sprint cannot be split partway through, how many more sprints will they need to clear the backlog?

  1. 6 sprints, based on dividing the 130-point backlog by the 21-point average velocity and rounding down to the nearest whole sprint
  2. 8 sprints, based on using only the most recent sprint's velocity of 18 points instead of the three-sprint average
  3. 7 sprints, based on dividing the 130-point backlog by the 21-point average velocity (63 total points across three sprints) and rounding up, since a partial sprint still requires a full extra sprint
  4. 3 sprints, based on dividing the backlog by the team's combined total velocity of 63 points from all three sprints, as if that combined total were a single sprint's rate

Answer: C. 7 sprints, based on dividing the 130-point backlog by the 21-point average velocity (63 total points across three sprints) and rounding up, since a partial sprint still requires a full extra sprint

ExplanationVelocity in Scrum is the amount of work (in story points) a team completes per sprint, and it's forecast using an average over several recent sprints, not a single sprint's number. Here the average velocity is (20 + 25 + 18) ÷ 3 = 63 ÷ 3 = 21 story points per sprint. To clear a 130-point backlog at that rate: 130 ÷ 21 ≈ 6.19 sprints. A team can't ship "0.19 of a sprint" — after 6 full sprints they'd have completed 6 × 21 = 126 points, leaving 4 points still undone, which forces a 7th sprint to finish. This rounding-up step is exactly why sprint forecasts in real Agile teams (and in exam questions about them) almost always come out as a ceiling, not a plain division answer. The tempting wrong turns are: stopping at the raw division result of 6 without accounting for the leftover 4 points; anchoring on just the latest sprint's velocity (18) instead of smoothing across sprints, which understates how much the team typically delivers and inflates the forecast to 8; and confusing "total points delivered so far" (63) with "points delivered per sprint," which would wrongly suggest the backlog clears in just 3 sprints.

Question 191 · Browser DevTools · hard

On an IRCTC train seat availability page, the HTML that first loads from the server contains the placeholder text "Checking availability...". A few seconds later, without reloading the page, a JavaScript fetch call runs in the background and rewrites that part of the page to show "12 seats available". A student opens Chrome DevTools right after this happens, first checks that spot using View Source (Ctrl+U), and then checks the same spot using the Elements panel. What does the student see in each place, and why?

  1. The Elements panel will display "12 seats available," because it continuously reflects the live DOM — including changes JavaScript makes after the page loads — while View Source only shows the original HTML delivered by the server.
  2. Both View Source and the Elements panel will display "Checking availability...," because DevTools always reads from the same cached HTML file the browser first downloaded, regardless of which panel is open.
  3. DevTools will show "Checking availability..." in the Elements panel too, since that panel only refreshes its snapshot of the DOM when the page is manually reloaded while DevTools stays open.
  4. View Source will display "12 seats available," because pressing Ctrl+U makes the browser re-fetch the page only after all of its JavaScript has finished executing.

Answer: A. The Elements panel will display "12 seats available," because it continuously reflects the live DOM — including changes JavaScript makes after the page loads — while View Source only shows the original HTML delivered by the server.

ExplanationView Source (Ctrl+U) requests the raw HTML document exactly as the server sent it, before any JavaScript ran — so it will always show "Checking availability...", the placeholder baked into that original file, no matter what happens on the page afterward. The Elements panel, by contrast, is not displaying a saved file at all; it renders the browser's current, in-memory DOM tree, which is kept continuously in sync with whatever JavaScript changes on the page. Since the fetch call already rewrote that node's text to "12 seats available" before the student opened DevTools, that is exactly what the Elements panel shows. This gap between "what the server originally sent" and "what is actually on screen right now" is precisely why View Source is unreliable for inspecting dynamic, JavaScript-driven pages, while the Elements panel is the tool built for that job — it updates live as scripts modify the DOM, it doesn't need a manual page reload to catch up, and Ctrl+U never re-fetches anything after scripts run.

Question 192 · IoT with Raspberry Pi: Connected Devices · hard

A Raspberry Pi in a smart greenhouse spins a cooling fan wired to GPIO18 using hardware PWM at 100 Hz. The function `set_fan_speed()` converts a temperature reading (in °C) into a duty cycle using `duty = 4 * (temp_c - 20)`, clamped between 0 and 100: ```python import RPi.GPIO as GPIO GPIO.setmode(GPIO.BCM) GPIO.setup(18, GPIO.OUT) fan = GPIO.PWM(18, 100) # 100 Hz PWM signal fan.start(0) def set_fan_speed(temp_c): duty = 4 * (temp_c - 20) duty = max(0, min(100, duty)) fan.ChangeDutyCycle(duty) set_fan_speed(32) ``` When the greenhouse's temperature sensor reads 32°C and `set_fan_speed(32)` runs, for how long does GPIO18 stay HIGH during each PWM cycle?

  1. 48 ms, since a 100 Hz PWM signal has a period of 100 ms and 48% of that period is HIGH
  2. 10 ms, since duty = (4 × 32) − 20 = 108%, which clamps to 100% and keeps the pin HIGH for the entire cycle
  3. 4.8 ms, since the duty cycle works out to 48% and the 100 Hz PWM signal has a 10 ms period
  4. 5.2 ms, since the pin spends the remaining 52% of each 10 ms cycle in the HIGH state before switching low

Answer: C. 4.8 ms, since the duty cycle works out to 48% and the 100 Hz PWM signal has a 10 ms period

ExplanationThe duty-cycle formula applies to the whole parenthesized expression: duty = 4 × (32 − 20) = 4 × 12 = 48, which already sits inside the 0–100 clamp, so `ChangeDutyCycle(48)` runs unchanged. A 100 Hz PWM signal repeats every 1 / 100 Hz = 10 ms, and a 48% duty cycle means GPIO18 is driven HIGH for 48% of that period: 0.48 × 10 ms = 4.8 ms, then LOW for the remaining 5.2 ms. The 48 ms distractor comes from confusing frequency with period (treating 100 Hz as if it meant a 100 ms cycle instead of taking its reciprocal). The 10 ms distractor comes from breaking operator precedence — computing 4 × 32 − 20 = 108 instead of 4 × (32 − 20) — which clamps to 100% and forces the pin fully HIGH. The 5.2 ms distractor swaps the ON and OFF portions of the cycle, reporting how long the pin is LOW rather than HIGH.

Question 193 · 3D Printing and Digital Fabrication · hard

A hobbyist in Bengaluru is 3D printing a storage cube on an FDM printer using a slicer app. The cube is 50 mm × 50 mm × 50 mm. The slicer is set to print a solid outer shell (perimeter walls) that is 5 mm thick on all six faces, and the hollow space left inside that shell is filled with 20% infill — meaning only 20% of that inner hollow volume is actually filled with printed lattice, the rest stays empty. What percentage of the cube's total volume ends up as actual printed plastic?

  1. 68.80%
  2. 59.04%
  3. 20%
  4. 41.68%

Answer: B. 59.04%

ExplanationThink of the printed cube as two separate regions: the solid outer shell, and the hollow interior that's only partly filled. Outer cube volume = 50 × 50 × 50 = 125,000 mm³. Because the shell is 5 mm thick on every face, it eats into the cube from both opposite sides along each dimension — so the inner hollow cube's side length is 50 − (5 + 5) = 40 mm, giving an inner volume of 40³ = 64,000 mm³. The shell itself is 100% solid plastic: 125,000 − 64,000 = 61,000 mm³. Of the 64,000 mm³ inner cavity, only 20% gets filled by the lattice: 0.20 × 64,000 = 12,800 mm³. Total printed plastic = 61,000 + 12,800 = 73,800 mm³. As a share of the whole cube: 73,800 ÷ 125,000 = 0.5904 = 59.04%. The concept a slicer actually uses: the infill percentage applies only to the hollow interior left over after the solid perimeter walls are printed — never to the entire part. Treating "20% infill" as "20% of the whole object" ignores the solid shell completely. Applying 20% to the full 125,000 mm³ instead of just the 64,000 mm³ cavity over-counts material, since it double-charges the wall region for infill it doesn't need. And subtracting the wall thickness from only one face (giving an inner side of 45 mm) instead of both opposing faces (giving 40 mm) undercounts how much of the cube the shell actually occupies.

Question 194 · Building a Developer Portfolio · hard

A CBSE Class 8 student is building a coding portfolio website and writes this JavaScript function to decide which projects appear in the "Featured Projects" section of the homepage: ```js const projects = [ { name: "Weather App", stars: 12, featured: true }, { name: "Chess AI", stars: 45, featured: false }, { name: "Portfolio Site", stars: 8, featured: true }, { name: "Todo List", stars: 30, featured: true }, { name: "Snake Game", stars: 19, featured: false } ]; function getHomepageProjects(list) { return list .filter(p => p.featured) .sort((a, b) => b.stars - a.stars) .slice(0, 2); } console.log(getHomepageProjects(projects).map(p => p.name)); ``` What does this code print to the console?

  1. ["Todo List", "Weather App"]
  2. ["Chess AI", "Todo List"]
  3. ["Portfolio Site", "Weather App"]
  4. ["Weather App", "Portfolio Site"]

Answer: A. ["Todo List", "Weather App"]

ExplanationFollow the three chained array methods in the order they actually run on the five-project list. filter(p => p.featured) keeps only projects with featured: true, removing Chess AI (45 stars, not featured) and Snake Game (19 stars, not featured). That leaves Weather App (12 stars), Portfolio Site (8 stars), and Todo List (30 stars), still in their original array order. Next, sort((a, b) => b.stars - a.stars) reorders this filtered list from highest star count to lowest — the comparator returns a negative number whenever b has more stars than a, which pushes b earlier — giving Todo List (30), Weather App (12), Portfolio Site (8). Finally, slice(0, 2) keeps only the first two entries of that sorted list, dropping Portfolio Site, and .map(p => p.name) pulls out just the names. So console.log prints ["Todo List", "Weather App"]: the two featured projects with the highest star counts, higher-starred one first. Sorting all five projects by stars without filtering first would wrongly let the unfeatured Chess AI through; sorting ascending instead of descending would put the lowest-starred projects first; and skipping the sort entirely would just preserve the filtered list's original array order instead of ranking by popularity.

Question 195 · Pair Programming Practices · hard

Aditi and Rohan are practicing the "ping-pong" style of pair programming while building a small calculator function together. The rule is simple: whoever makes a failing test pass immediately writes the next failing test, and their partner then attempts to make that new test pass. Aditi writes Test 1, and it fails as expected. Rohan makes Test 1 pass. Following this rule strictly through Test 4, which of these correctly describes what happens with Test 3 and Test 4?

  1. The rule sends both actions to Rohan: he passes Test 3, then immediately writes Test 4 as well.
  2. Aditi passes Test 3, and Rohan is the one who writes Test 4 next in the sequence.
  3. Roles swap strictly by test number, so Aditi writes Test 4 even though Rohan passed Test 3.
  4. One partner, Aditi, stays in control of both passing Test 3 and writing Test 4 until a bug shows up.

Answer: A. The rule sends both actions to Rohan: he passes Test 3, then immediately writes Test 4 as well.

ExplanationIn the ping-pong style of pair programming, whose turn it is depends on who last made a test pass — not on a fixed rotation and not on whether the test number is odd or even. The rule is: pass a test, then immediately write the next failing test for your partner to solve. Tracing the sequence from the start makes this concrete. Aditi writes Test 1, which fails, and Rohan makes it pass — so by the rule, Rohan writes Test 2. Aditi then makes Test 2 pass, so Aditi writes Test 3. Rohan makes Test 3 pass, so Rohan writes Test 4. Notice that the writer of any test is always the person who passed the previous one, so the same partner naturally does two jobs back to back: Rohan passes Test 3 and then writes Test 4. This is what separates ping-pong pairing from plain driver/navigator pairing, where the two roles typically switch on a fixed timer (say, every 10 minutes) regardless of what just happened in the code — here, the switch is triggered entirely by who resolved the previous test.

Question 196 · Mobile App Development: From Idea to Store · hard

Priya is publishing her Android app "StudyBuddy" to the Google Play Store. Over four weeks she uploads these builds, in this exact order, each one submitted only after the previous upload was accepted: Upload 1 — versionName "1.0.0", versionCode 1 Upload 2 — versionName "1.0.1", versionCode 2 Upload 3 — versionName "1.1.0", versionCode 4 Upload 4 — versionName "1.1.1" (an urgent hotfix), versionCode 3 Which of these four uploads does the Play Console reject, and why?

  1. Upload 3 (versionCode 4) is rejected because it skips versionCode 3, and Play Console requires versionCode values to increase in an unbroken sequence with no gaps.
  2. Upload 4 (versionCode 3) is rejected because 3 is not greater than 4, the highest versionCode already accepted for this app — even though its versionName "1.1.1" is a semantically newer release than "1.1.0".
  3. Upload 2 (versionCode 2) is rejected because Play Console does not allow two separate uploads to be submitted within the same calendar week.
  4. None of the four uploads are rejected, since Play Console only validates the versionName string shown to users on the store listing and ignores versionCode entirely.

Answer: B. Upload 4 (versionCode 3) is rejected because 3 is not greater than 4, the highest versionCode already accepted for this app — even though its versionName "1.1.1" is a semantically newer release than "1.1.0".

ExplanationEvery Android build carries two separate version fields: versionName, the human-readable string like "1.1.1" shown on the store page, and versionCode, a plain integer the Play Console uses internally to decide upload order. The rule it enforces has nothing to do with semantic versioning — it only tracks the single highest versionCode ever accepted for that app, and every new upload's versionCode must be strictly greater than that running maximum. Tracing Priya's four uploads: after upload 1 the accepted maximum is 1, after upload 2 it is 2, and after upload 3 it is 4 (skipping 3 is perfectly legal — the rule is "strictly increasing," not "consecutive," so no gap check ever fires, which rules out treating upload 3 as the failure). When upload 4 arrives with versionCode 3, the console compares 3 against the current maximum of 4. Since 3 is not greater than 4, the upload is rejected — regardless of the fact that "1.1.1" reads as newer than "1.1.0" to a human. To get the hotfix accepted, Priya must rebuild it with versionCode 5 or higher; the versionName string can stay "1.1.1" or even go back to something that looks smaller, since Play Console never inspects it for ordering. There's also no such thing as a same-day or same-week upload limit, so nothing rejects upload 2 either.

Question 197 · Machine Learning Foundations: Teaching Computers · hard

A Grade 8 student in Bengaluru is building a machine learning model to automatically flag spam SMS messages (the "Congrats! You've WON ₹50,000, click here" type) forwarded in a college WhatsApp/SMS group. She trains two candidate models on the same 200 labelled training messages, then checks both models on a separate set of 150 new messages that neither model has seen before: Model A: correctly classifies 180 of the 200 training messages, and 132 of the 150 test messages. Model B: correctly classifies all 200 of the 200 training messages, and 90 of the 150 test messages. Based on these results, which model should she deploy to catch spam in messages the system hasn't seen before, and why?

  1. Deploy Model B — a classifier that correctly labels all 200 of its training messages has proven it has learned the true spam-detection rule, so its lower score on the new 150 messages must reflect an unusually hard test batch rather than a flaw in the model.
  2. Deploy either model — averaging each one's training and test accuracy gives Model A about 89% and Model B about 80%, and a 9-point gap in this overall figure is too small to justify picking one system over the other.
  3. Deploy Model A — its accuracy barely drops from training to test (90% to 88%), while Model B's collapses (100% to 60%), showing Model B memorised the exact wording of the 200 training messages instead of learning spam patterns that generalise to messages it has never seen.
  4. Deploy Model B — its training accuracy is calculated from 200 messages, more than the 150 messages in the test set, so the 100% figure rests on more evidence and should be trusted over Model A's smaller test result.

Answer: C. Deploy Model A — its accuracy barely drops from training to test (90% to 88%), while Model B's collapses (100% to 60%), showing Model B memorised the exact wording of the 200 training messages instead of learning spam patterns that generalise to messages it has never seen.

ExplanationModel A is the better choice for deployment, because what matters is how a model performs on data it has never encountered — exactly what the 150-message test set measures. Model A's accuracy barely changes between training and test: 90% (180/200) versus 88% (132/150), a two-point gap. Model B's accuracy collapses from a perfect 100% (200/200) on training data to just 60% (90/150) on the test data, a forty-point gap. A model that scores perfectly on the data it was trained on but performs far worse on new data is exhibiting overfitting: instead of learning general features of spam (words like "WON", "click here", suspicious links), Model B essentially memorised the exact wording, senders, and quirks of the 200 training messages, none of which repeat in new messages. A 100% training score does not prove a model has learned the "true rule" — it can just as easily mean the model has memorised noise specific to that data, which is why trusting training accuracy alone, or trusting it because it came from a larger sample (200 vs. 150), is misleading; the size of the training set does not make an overfit score more reliable. Averaging training and test accuracy also hides the real story, since it treats memorisation and genuine generalisation as equally meaningful when they are not. Model A's small, stable gap between training and test performance is the real evidence that it has learned patterns that will keep working on spam messages it hasn't seen yet.

Question 198 · Quantum Computing: The Future of Computation · hard

Suppose an unsorted database of N = 1,000,000 unique IRCTC PNR records must be searched for one specific record, with a classical computer needing to check records one by one (up to N steps in the worst case) while Grover's quantum search algorithm can find it in roughly √N steps — approximately how many steps does Grover's algorithm need here, and how does this compare to the classical worst case?

  1. About 1,000 steps, since √1,000,000 = 1,000 — roughly 1,000 times fewer steps than the up to 1,000,000 steps a classical worst-case search would need.
  2. About 20 steps, since log₂(1,000,000) ≈ 20 — quantum computers give an exponential speedup on any search problem, the same way Shor's algorithm speeds up factoring.
  3. About 1,000,000 steps — superposition lets the qubits represent every record at once, but since measuring the system collapses it to a single outcome, Grover's algorithm ends up with no real speed advantage over classical search.
  4. About 500,000 steps — a qubit's superposition of two states effectively doubles the search speed, cutting the classical worst-case step count in half.

Answer: A. About 1,000 steps, since √1,000,000 = 1,000 — roughly 1,000 times fewer steps than the up to 1,000,000 steps a classical worst-case search would need.

ExplanationGrover's algorithm achieves what's called a quadratic speedup: for N items, it needs on the order of √N steps instead of N. Here N = 1,000,000 IRCTC PNR records, so √1,000,000 = 1,000 — meaning about 1,000 amplitude-amplification steps are enough to find the target record with high probability, compared to up to 1,000,000 checks a classical computer might need if it inspects records one at a time in the worst case. That's roughly a 1,000-fold reduction in steps, not the exponential (log N ≈ 20) speedup that algorithms like Shor's factoring algorithm provide — Grover's search is famously powerful, but only quadratically so, and mixing it up with Shor's exponential speedup is a common mistake. The "no advantage" idea is also wrong: although measurement does collapse a superposition to a single outcome, Grover's algorithm uses interference to amplify the probability amplitude of the correct answer across those ~1,000 rounds before that final measurement, which is precisely how it beats classical search rather than gaining nothing from it. And the speedup doesn't come from a simple "one qubit doubles the speed" idea either — the √N scaling comes from how many amplitude-amplification rounds are needed to boost the right answer's probability close to 1, not from counting states two at a time.

Question 199 · Digital Privacy Rights: Protecting Your Information · hard

A fitness-tracking app popular among Indian students requests permission to access your location, step count, and heart rate, stating that this data will be used only to show personalized daily health statistics. You grant this permission. Six months later, without asking for any new consent, the app starts using your saved location history to send targeted advertisements for nearby gyms and restaurants, and shares this location data with third-party advertising companies. Which specific data privacy principle has this app violated?

  1. Data minimization, because the app should have collected less data than location, step count, and heart rate to build daily health statistics
  2. Purpose limitation, because the app is reusing data collected for health tracking for a new purpose, advertising, without asking for fresh consent
  3. Right to erasure, because the app never gave the user an option to permanently delete their account and stored data
  4. Data portability, because the user was never given a way to export their health data in a format usable by other fitness apps

Answer: B. Purpose limitation, because the app is reusing data collected for health tracking for a new purpose, advertising, without asking for fresh consent

ExplanationThe scenario describes data being collected for one clearly stated reason, personalized health statistics, and then reused for a completely different reason, advertising, and handed to outside companies, all without the app going back to ask permission again. This is precisely what purpose limitation protects against: once a user consents to their data being used for a specific, named purpose, that data cannot be repurposed for something unrelated, such as marketing, unless fresh and specific consent is obtained for the new use. The problem here is not the amount of data gathered, since location, step count, and heart rate are all reasonable inputs for a health-tracking feature, which rules out a data minimization issue. Nothing in the scenario involves the user trying to delete their account or stored information, so no erasure request has been denied, and nothing involves the user trying to move their data to a competing app, so no portability request has been ignored either. Under India's Digital Personal Data Protection Act, 2023, and privacy frameworks used worldwide, consent is always tied to the specific purpose it was given for, and any expansion of that purpose requires the data fiduciary to return to the user for permission.

Question 200 · Algorithm Complexity: Big O Notation · hard

Study the following Python function, which uses an outer loop that doubles its counter on every pass and an inner loop whose length depends on the current counter value: ```python def mystery(n): count = 0 i = 1 while i < n: for j in range(i): count += 1 i = i * 2 return count ``` What is the time complexity of `mystery(n)` in terms of n?

  1. O(n log n), because the outer loop runs about log n times (since i doubles until it passes n) and the inner loop can run up to n times, so multiplying the two loop bounds together gives n log n
  2. O(n), because the inner loop's total work across all outer iterations forms a geometric series (1 + 2 + 4 + ... up to about n), and such a series always sums to a value proportional to n
  3. O(log n), because the outer loop runs far fewer times than the inner loop could, so the outer loop alone determines how the running time grows
  4. O(n²), because the function contains one loop nested inside another, and any nested loop structure always produces quadratic running time

Answer: B. O(n), because the inner loop's total work across all outer iterations forms a geometric series (1 + 2 + 4 + ... up to about n), and such a series always sums to a value proportional to n

ExplanationThe inner loop performs exactly i operations on every pass of the outer loop, and i doubles each time — 1, 2, 4, 8, and so on — until it passes n, which is why the outer loop itself runs only about log n times. Summing a doubling sequence like 1 + 2 + 4 + ... up to roughly n is a geometric series, and the defining property of such a series is that its total stays proportional to the largest term alone, which here is close to n. That means the total number of times `count += 1` executes across the whole function scales directly with n rather than with n multiplied by log n, so multiplying the outer loop's log n passes by the inner loop's largest possible size overcounts the work, since the inner loop stays far smaller than n for most of the run. Assuming the outer loop's small iteration count determines everything ignores how much work each of those iterations actually performs, and assuming any nested-loop structure must be quadratic ignores that the inner loop here only reaches a size close to n on its very last pass rather than on every pass.
← Set 9Set 11 →