Goal
This lab turns the week 3 notes into working code. By the end you have:
- the five logic gates and a brute-force equivalence checker that finds counterexamples,
- a half adder, a full adder and a ripple-carry adder that adds binary numbers using nothing but gates,
- NOT, AND, OR and XOR rebuilt from NAND alone,
- a Knights and Knaves solver that cracks any puzzle you can write down, and a consistency checker for requirements.
Steps
Join the course organization (once)
⏱ 2 minYou received an email from GitHub at your university address (your-student-number@firat.edu.tr), titled “… invited you to join the FiratUniversity-IJDP-SoftEng organization”.
- Open the email and click Join FiratUniversity-IJDP-SoftEng.
- Sign in with your GitHub account (the one you used for Lab 1) and click Join.
That is all. You are now on the course roster.
Accept Lab 2
⏱ 1 min- Click the orange Accept lab lab02 button at the top of this page.
- If asked, click Sign in with GitHub and authorize Classroom 50.
- Click Accept assignment and wait for the checklist to finish (about 20 seconds).
- Click Open repository.
You now have a private repository called discrete-mathematics-lab02-yourusername inside the course organization, with the starter code, the tests and the notebook. Only you and the instructor can see it.
Open it in Codespaces
⏱ 2 minOn your repository page: green Code button → Codespaces tab → Create codespace on main. Exactly like Lab 1; the first start takes a minute or two.
Look around and run the checks
⏱ 3 min| File | What it is |
|---|---|
circuits.py | your work, part 1: gates, equivalence, adders, NAND |
puzzles.py | your work, part 2: Knights & Knaves, requirements |
lab02.ipynb | playground notebook, not graded |
check.py, tests/ | the checks (do not edit the tests) |
$ python check.py:| the gate matches its truth table and returns 0/1 [AND]
not implemented yet (still raises NotImplementedError)
...
0/34 checks passed In circuits.py a bit is the integer 1 or 0, like a wire. In puzzles.py truth values are True and False. Keep that in mind; it is the most common source of surprises.
Part A: logic gates
⏱ 5 minWrite NOT, AND, OR, XOR and NAND in circuits.py. Each takes bits and must return 0 or 1, not True or False.
Two ways to think about it: with conditions (return 1 if a and b else 0) or with arithmetic on bits (1 - a is NOT). For NAND, reuse your own gates: NAND is NOT of AND.
Part B: equivalence by brute force
⏱ 10 mincounterexample(f, g, n) walks through every row of n bits and returns the first row where f and g disagree, or None. It is the equivalence checker from the notes, in eight lines.
from itertools import product
for row in product([1, 0], repeat=2):
print(row) # (1, 1), (1, 0), (0, 1), (0, 0)Then equivalent(f, g, n) is one line: two formulas are equivalent exactly when there is no counterexample.
$ python circuits.py...
p→q ≡ ¬q→¬p ? True
p→q ≡ q→p ? counterexample: (1, 0) Part C: adders from gates
⏱ 20 minUse only your gate functions: the checks reject +, -, // and % in the adders.
half_adder(a, b)returns(sum, carry): sum is XOR, carry is AND. One line.full_adder(a, b, cin)returns(sum, carry_out). Either use the two formulas in the docstring, or chain two half adders and one OR (the circuit on the week 3 page).add_binary(x, y)adds two bit strings like a CPU does:- pad the shorter string with
"0"on the left (str.zfillhelps), - walk the columns from right to left with
reversed(...), starting with carry0, - each column:
s, carry = full_adder(int(a), int(b), carry), - if a carry is left at the end, it becomes an extra leading
"1".
- pad the shorter string with
$ python circuits.pyhalf_adder(1, 1) = (0, 1)
11 + 6 = 10001 (binary) The checks add all 256 pairs of 4-bit numbers with your adder. If one pair fails, the message tells you which.
Part D: NAND is enough
⏱ 10 minRebuild NOT, AND, OR and XOR using only calls to NAND: no other gates, no and/or/not, no operators, no if. The checks read your code to make sure. The identities are in the notes:
For XOR, start with n = NAND(a, b); four NAND calls are enough.
Part E: a Knights and Knaves solver
⏱ 15 minOpen puzzles.py. A world says who is a knight: {"A": True, "B": False}. A statement is a function of the world:
"A": lambda w: w["B"] # A says: "B is a knight"
"B": lambda w: w["A"] != w["B"] # B says: "We are of opposite types"solve(people, statements) tries every world and keeps the consistent ones. The one rule from the lecture: a speaker is a knight if and only if their statement is true, so for every speaker s, w[s] == statements[s](w) must hold.
- Loop over
product([True, False], repeat=len(people)). - Turn each tuple into a world with
dict(zip(people, values)). - Keep the world if the rule holds for all speakers (
all(...)is ∀ again).
Part F: write the puzzles
⏱ 10 minTranslate each puzzle into a statements dict. Then run python puzzles.py and check the answers make sense.
- Puzzle 1 is the one from the notes: both should be knaves.
- Puzzle 2: A says “at least one of us is a knave”. B says nothing, so B has no entry.
- Puzzle 3: A says “all three of us are knaves”, B says “exactly one of us is a knight”. Hint:
[w["A"], w["B"], w["C"]].count(True) == 1.
Part G: are the requirements consistent?
⏱ 10 minsatisfying_row(formulas, n) returns the first row that makes every formula true, or None. login_requirements() returns the four requirements from the week 3 notes as functions of (p, q, r).
Translate carefully: requirement 1 says access is granted only if the user is authenticated. Which way does the arrow point? (Week 2: “ only if ” is .)
$ python puzzles.py...
requirements 1–3 consistent: (True, True, False)
requirements 1–4 consistent: None Submit
⏱ 1 minSubmitting is pushing. Every push is graded automatically.
$ git add -A$ git commit -m "Lab 2"$ git push About two minutes later:
- On your repository page, look at Releases on the right. The newest
submit/…release shows your score and a table of passed and failed checks. - Or open our classroom on Classroom 50 → Lab 2 → My submission.
Push as often as you like before Friday 23:59; the last push before the deadline counts.
Other ways to work and submit
Codespaces is the easiest route, but it is not the only one. You can write your code anywhere and hand it in from the browser. Grading is exactly the same: every change that lands in your repository is graded automatically.
Where you can work
| Option | What to do |
|---|---|
| Your own computer | Install Python 3.12+ and an editor such as VS Code. Download circuits.py, puzzles.py, check.py and the tests folder from your repository (green Code button → Download ZIP), unzip, then run pip install pytest and python check.py. |
| cs50.dev | Open cs50.dev, create circuits.py and puzzles.py, paste the starter code from your repository and work there. Run python circuits.py and python puzzles.py to try your functions. |
| Google Colab or any online Python | Paste one function at a time into a cell and test it on a few inputs. Good for experimenting; the official checks run on GitHub. |
| github.dev | On your repository page press the . key. A light VS Code opens in the browser for editing (it cannot run Python, but you can commit from it). |
Submit from the browser (no terminal, no git)
When your solution is ready in a file on your computer or in another editor, put it into your repository on github.com in one of two ways.
A. Paste into the file
- Open your repository
discrete-mathematics-lab02-yourusernameon github.com. - Click
circuits.py, then the pencil icon ✎ (Edit this file) at the top right of the file. - Select everything (Ctrl + A, or ⌘ + A on a Mac) and paste your complete file.
- Click Commit changes…, write a short message such as
Lab 2 circuits, keep Commit directly to the main branch selected, and click Commit changes. - Do the same for
puzzles.py.
B. Upload the files
- On your repository page click Add file → Upload files.
- Drag
circuits.pyandpuzzles.pyfrom your computer into the box. Files with the same name replace the starter files; that is what you want. - Click Commit changes.
Check your grade the same way. About two minutes after each commit, a new Release with your score appears on the right of your repository page. Every commit counts as a submission, the last one before Friday 23:59 is the one that matters, and you may mix methods (for example, push from Codespaces on Monday and paste a fix from home on Wednesday).
Explore: the notebook
lab02.ipynb is not graded. Encrypt a message with XOR and see why reusing a key is fatal, rebuild a dead RAID disk from parity, count how many NAND gates a full adder costs, and watch brute force grow exponentially.
Lab 2 · Explore notebook
Read it here, run it in Colab or in your cs50.dev workspace.
Challenges (optional, not graded)
- Subtraction. Two’s complement turns into . Write
sub_binary(x, y)with your gates andadd_binary. - Comparator. Write
greater(x, y)for 4-bit strings using only gates. - A harder island. C says “A and B are of the same type”. A says “C is a knave”. What are they? Let your solver answer.