Hey, I have a chem final tomorrow my teacher gave questions to an AI and asked it to make a study guide based on the question and this is the attached one it made. I am the dumbest person in chemistry and I have no clue about anything in this. I want you to go through everything in the study guide and teach me and make me ready for the test tomorrow. my goal is to get 80 in tests. Also keep in mind that I am allowed to have one A4 size paper as cheat sheet to have with me during test
cd /home/daytona/workspace/attachments/c1917670-4af0-466e-9706-58a2ebf9f687 && python3 -c " import pdfplumber with pdfplumber.open('HonorsChem_StudyGuide (2).pdf') as pdf: print(f'Total pages: {len(pdf.pages)}') for i, page in enumerate(pdf.pages): print(f'\\n===== PAGE {i+1} =====') print(page.extract_text()) "
| Word | What it means |
|---|---|
| Ground state | The atom is "relaxed" — electrons are in the lowest possible energy positions |
| Excited state | An electron absorbed energy and jumped to a higher level (like a kid who ate candy) |
| Isotopes | Same element, different number of neutrons → different mass. Example: Carbon-12 vs Carbon-14 (both are carbon, both have 6 protons, but different neutrons) |
| Allotropes | Same element, different structure. Diamond and graphite are both pure carbon — just arranged differently |
| Alloy | A mixture that has metallic properties. Steel = iron + carbon. Bronze = copper + tin |
⚠️ 4s fills BEFORE 3d — this trips everyone up!
[Ar] 4s² 3d¹⁰ 4p²c = λν → rearranged: ν = c / λ
E = hν → or: E = hc / λ
ν = c/λ = (3.00 × 10⁸) / (5.00 × 10⁻⁷) = 6.00 × 10¹⁴ Hz
| Bond Type | How it works | Who does it | Example |
|---|---|---|---|
| Ionic | Metal gives electrons to nonmetal | Metal + Nonmetal | NaCl (table salt) |
| Covalent | Two nonmetals SHARE electrons | Nonmetal + Nonmetal | H₂O, CO₂ |
| Coordinate covalent | ONE atom donates BOTH electrons | Special case of covalent | NH₄⁺ |
| Bond | σ bonds | π bonds |
|---|---|---|
| Single (C–C) | 1 | 0 |
| Double (C=C) | 1 | 1 |
| Triple (C≡C) | 1 | 2 |
| Ion | Name | Memory trick |
|---|---|---|
| SO₄²⁻ | sulfate | more O = -ate |
| SO₃²⁻ | sulfite | less O = -ite |
| NO₃⁻ | nitrate | |
| NO₂⁻ | nitrite | |
| PO₄³⁻ | phosphate | |
| OH⁻ | hydroxide | |
| NH₄⁺ | ammonium | + charge (rare!) |
| ClO⁻ | hypochlorite | hypo = fewest O |
| ClO₂⁻ | chlorite | |
| ClO₃⁻ | chlorate | |
| ClO₄⁻ | perchlorate | per = most O |
| # | Prefix |
|---|---|
| 1 | mono- |
| 2 | di- |
| 3 | tri- |
| 4 | tetra- |
| 5 | penta- |
| 6 | hexa- |
| 7 | hepta- |
| 8 | octa- |
grams ÷ molar mass = moles
moles × molar mass = grams
moles × 6.02×10²³ = number of particles
% element = (mass of element in formula / molar mass of compound) × 100
% yield = (actual yield / theoretical yield) × 100
q = m × c × ΔT
q = 30.0 × 0.900 × (40 − 20) = 30.0 × 0.900 × 20 = 540 J
If ΔH < 0 (exothermic) AND ΔS > 0 (more disorder) → ALWAYS spontaneous at any temperature. ΔG is always negative.
| Factor | Effect | Why |
|---|---|---|
| ↑ Temperature | Rate increases | Particles move faster → more collisions with enough energy |
| Add a catalyst | Rate increases | Lowers the activation energy (lower energy hill) |
| ↑ Concentration | Rate increases | More particles → more collisions |
| ↑ Surface area | Rate increases | More particles exposed to react |
mkdir -p /home/daytona/workspace/chem-cheatsheet && ls /home/daytona/workspace/chem-cheatsheet
~/chem-cheatsheet/make_cheatsheet.py
from reportlab.lib.pagesizes import A4
from reportlab.lib.units import cm, mm
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib import colors
from reportlab.platypus import (SimpleDocTemplate, Paragraph, Table, TableStyle,
Spacer, HRFlowable, KeepTogether)
from reportlab.lib.enums import TA_CENTER, TA_LEFT
import os
output_path = "/home/daytona/workspace/chem-cheatsheet/HonorsChem_CheatSheet.pdf"
doc = SimpleDocTemplate(
output_path,
pagesize=A4,
leftMargin=0.7*cm,
rightMargin=0.7*cm,
topMargin=0.6*cm,
bottomMargin=0.6*cm,
)
W, H = A4
# ── Color palette ──────────────────────────────────────────────
DARK_BLUE = colors.HexColor("#1a3a5c")
MED_BLUE = colors.HexColor("#2563a8")
LIGHT_BLUE = colors.HexColor("#dbeafe")
ACCENT = colors.HexColor("#e76f00")
PALE_YELLOW = colors.HexColor("#fffbe6")
PALE_GREEN = colors.HexColor("#eafaf1")
PALE_PINK = colors.HexColor("#fdecea")
WHITE = colors.white
LIGHT_GRAY = colors.HexColor("#f0f4f8")
# ── Base styles ─────────────────────────────────────────────────
def sty(name, parent="Normal", **kw):
s = ParagraphStyle(name, parent=getSampleStyleSheet()[parent])
for k, v in kw.items():
setattr(s, k, v)
return s
title_sty = sty("title", fontSize=11, textColor=WHITE, fontName="Helvetica-Bold",
alignment=TA_CENTER, spaceAfter=1, spaceBefore=1, leading=13)
sec_sty = sty("sec", fontSize=7.5, textColor=DARK_BLUE, fontName="Helvetica-Bold",
alignment=TA_CENTER, spaceAfter=0, spaceBefore=0, leading=9)
body_sty = sty("body", fontSize=6.5, fontName="Helvetica",
spaceAfter=0, spaceBefore=0, leading=8)
bold_sty = sty("bold", fontSize=6.5, fontName="Helvetica-Bold",
spaceAfter=0, spaceBefore=0, leading=8)
formula_sty = sty("formula",fontSize=6.5, fontName="Courier-Bold", textColor=MED_BLUE,
spaceAfter=0, spaceBefore=0, leading=8)
def P(txt, s=body_sty):
return Paragraph(txt, s)
def header_row(label, bg=MED_BLUE):
return [Paragraph(f"<b>{label}</b>", title_sty)]
def section_table(label, content_rows, col_widths, bg=LIGHT_BLUE, hdr_bg=MED_BLUE, rowbg=None):
"""Build a titled section table."""
# header
data = [[Paragraph(f"<b>{label}</b>", title_sty)]] + content_rows
cw = col_widths
ncols = len(col_widths)
style = [
# header
("BACKGROUND", (0,0), (-1,0), hdr_bg),
("SPAN", (0,0), (-1,0)),
("ALIGN", (0,0), (-1,0), "CENTER"),
("TOPPADDING", (0,0), (-1,0), 2),
("BOTTOMPADDING",(0,0),(-1,0),2),
# body
("FONTNAME", (0,1), (-1,-1), "Helvetica"),
("FONTSIZE", (0,1), (-1,-1), 6.2),
("LEADING", (0,1), (-1,-1), 7.5),
("TOPPADDING", (0,1), (-1,-1), 1.2),
("BOTTOMPADDING",(0,1),(-1,-1),1.2),
("LEFTPADDING",(0,0), (-1,-1), 2.5),
("RIGHTPADDING",(0,0),(-1,-1),2.5),
("ROWBACKGROUNDS",(0,1),(-1,-1),[WHITE, LIGHT_GRAY]),
("BOX", (0,0), (-1,-1), 0.4, MED_BLUE),
("INNERGRID", (0,1), (-1,-1), 0.2, colors.HexColor("#c8d8e8")),
("VALIGN", (0,0), (-1,-1), "MIDDLE"),
]
if rowbg:
for i, c in enumerate(rowbg, start=1):
if c:
style.append(("BACKGROUND",(0,i),(-1,i),c))
t = Table(data, colWidths=cw)
t.setStyle(TableStyle(style))
return t
# ══════════════════════════════════════════════════════
# Build content in two columns
# ══════════════════════════════════════════════════════
usable_w = W - 1.4*cm # total usable width
col_w = (usable_w - 3*mm) / 2 # each column (2 cols with small gap)
# helper to combine two blocks side by side
def two_col(left_items, right_items):
left_str = ""
right_str = ""
# We'll use a Table with two columns, each holding a sub-table
L = [left_items] if not isinstance(left_items, list) else left_items
R = [right_items] if not isinstance(right_items, list) else right_items
inner = Table([[L, R]], colWidths=[col_w, col_w])
inner.setStyle(TableStyle([
("VALIGN", (0,0),(-1,-1),"TOP"),
("LEFTPADDING", (0,0),(-1,-1),0),
("RIGHTPADDING",(0,0),(-1,-1),0),
("TOPPADDING", (0,0),(-1,-1),0),
("BOTTOMPADDING",(0,0),(-1,-1),0),
]))
return inner
# ── Spacer shorthand ───────────────────────────────────
SP = Spacer(1, 1.5*mm)
# ═══════════════════════════════════════════════════════
# SECTION 1 — Formulas (left col)
# ═══════════════════════════════════════════════════════
def formulas_block():
rows = [
[P("<b>Photon freq</b>"), P("ν = c / λ (c=3×10⁸ m/s)", formula_sty)],
[P("<b>Photon energy</b>"), P("E = hν = hc/λ (h=6.626×10⁻³⁴)", formula_sty)],
[P("<b>Specific heat</b>"), P("q = m × c × ΔT", formula_sty)],
[P("<b>Calorie conv</b>"), P("1 Cal = 4184 J", formula_sty)],
[P("<b>Moles</b>"), P("n = mass / molar mass", formula_sty)],
[P("<b>% composition</b>"), P("% = (elem mass / MM) × 100", formula_sty)],
[P("<b>% yield</b>"), P("% yield = actual/theoretical × 100", formula_sty)],
[P("<b>Avogadro</b>"), P("6.02 × 10²³ particles/mol", formula_sty)],
]
return section_table("🔢 KEY FORMULAS (Qs 3–9, 19–26)", rows,
[col_w*0.38, col_w*0.62], hdr_bg=DARK_BLUE)
# ═══════════════════════════════════════════════════════
# SECTION 2 — Polyatomic Ions (right col)
# ═══════════════════════════════════════════════════════
def ions_block():
rows = [
[P("<b>Ion</b>", bold_sty), P("<b>Name</b>", bold_sty), P("<b>Ion</b>", bold_sty), P("<b>Name</b>", bold_sty)],
[P("SO₄²⁻"), P("sulfate"), P("ClO⁻"), P("hypochlorite")],
[P("SO₃²⁻"), P("sulfite"), P("ClO₂⁻"), P("chlorite")],
[P("NO₃⁻"), P("nitrate"), P("ClO₃⁻"), P("chlorate")],
[P("NO₂⁻"), P("nitrite"), P("ClO₄⁻"), P("perchlorate")],
[P("PO₄³⁻"), P("phosphate"), P("OH⁻"), P("hydroxide")],
[P("NH₄⁺"), P("ammonium"), P("CO₃²⁻"), P("carbonate")],
]
style_extra = [("BACKGROUND",(0,1),(-1,1), LIGHT_BLUE),
("FONTNAME",(0,1),(-1,1),"Helvetica-Bold")]
cws = [col_w*0.18, col_w*0.32, col_w*0.18, col_w*0.32]
t = section_table("⚡ POLYATOMIC IONS", rows, cws, hdr_bg=colors.HexColor("#7c3aed"))
return t
# ═══════════════════════════════════════════════════════
# SECTION 3 — Bonding & Geometry (left)
# ═══════════════════════════════════════════════════════
def bonding_block():
rows = [
[P("<b>Ionic</b>"), P("Metal+Nonmetal, e⁻ transferred, high MP")],
[P("<b>Covalent</b>"), P("Nonmetal+Nonmetal, e⁻ shared")],
[P("<b>Coord. cov</b>"), P("ONE atom donates BOTH e⁻")],
[P("<b>Single</b>"), P("1σ, 0π")],
[P("<b>Double</b>"), P("1σ, 1π (e.g. C₂H₄: 5σ 1π)")],
[P("<b>Triple</b>"), P("1σ, 2π — shortest & strongest")],
[P("<b>Nonpolar</b>"), P("Symmetric (SiH₄ tetrahedral)")],
[P("<b>Polar</b>"), P("Lone pairs on center (AsH₃,H₂S)")],
[P("<b>HOFBrINCl</b>"), P("Diatomic elements: H₂O₂F₂Br₂I₂N₂Cl₂")],
]
return section_table("🔗 BONDING (Qs 32–40)", rows,
[col_w*0.28, col_w*0.72], hdr_bg=colors.HexColor("#0f766e"))
# ═══════════════════════════════════════════════════════
# SECTION 4 — Nomenclature (right)
# ═══════════════════════════════════════════════════════
def nomenclature_block():
rows = [
[P("<b>Variable-charge metal</b>"), P("Use Roman numerals: Fe₂(SO₄)₃ = Iron(III) sulfate")],
[P("<b>Crossover rule</b>"), P("Ca²⁺+PO₄³⁻ → Ca₃(PO₄)₂ (cross charges)")],
[P("<b>Hydrate</b>"), P("Compound•xH₂O e.g. Ba(OH)₂•8H₂O")],
[P("<b>Molecular prefixes</b>"), P("mono di tri tetra penta hexa hepta octa")],
[P("<b>Acid -ate ion</b>"), P("-ic acid: H₂SO₄ = sulfuric acid")],
[P("<b>Acid -ite ion</b>"), P("-ous acid: H₂SO₃ = sulfurous acid")],
[P("<b>N₂H₄</b>"), P("dinitrogen tetrahydride")],
]
return section_table("📛 NOMENCLATURE (Qs 27,35,36,41–43)", rows,
[col_w*0.33, col_w*0.67], hdr_bg=colors.HexColor("#b45309"))
# ═══════════════════════════════════════════════════════
# SECTION 5 — Atomic Structure (left)
# ═══════════════════════════════════════════════════════
def atomic_block():
rows = [
[P("<b>Ground state</b>"), P("Lowest energy — electrons at rest")],
[P("<b>Excited state</b>"), P("Electron absorbed energy → jumped up")],
[P("<b>Isotopes</b>"), P("Same element, different # neutrons")],
[P("<b>Allotropes</b>"), P("Same element, diff structure (C: diamond/graphite)")],
[P("<b>Alloy</b>"), P("Metal mixture (steel=Fe+C, bronze=Cu+Sn)")],
[P("<b>Fill order</b>"), P("1s 2s 2p 3s 3p <b>4s</b> 3d 4p 5s 4d 5p …", body_sty)],
[P("<b>Ge config</b>"), P("[Ar] 4s² 3d¹⁰ 4p² (Period 4, Group 14)")],
[P("<b>Octet rule</b>"), P("Stable = 8 electrons in outer shell")],
]
return section_table("⚛ ATOMIC STRUCTURE (Qs 44–50)", rows,
[col_w*0.28, col_w*0.72], hdr_bg=colors.HexColor("#6d28d9"))
# ═══════════════════════════════════════════════════════
# SECTION 6 — Mole/Stoich (right)
# ═══════════════════════════════════════════════════════
def mole_block():
rows = [
[P("<b>moles from g</b>"), P("n = mass ÷ molar mass")],
[P("<b>g from moles</b>"), P("g = mol × molar mass")],
[P("<b>Empirical formula</b>"), P("% → g → ÷ atomic mass → ÷ smallest → ratio")],
[P("<b>CO₂ % C</b>"), P("12/44×100 = 27.3% C")],
[P("<b>Fe₂O₃ MM</b>"), P("2(56)+3(16) = 159.7 g/mol → 0.82 mol = 131 g")],
[P("<b>Cl₂ (100 g)</b>"), P("100÷71 = 1.41 mol")],
[P("<b>H₂CrO₄ MM</b>"), P("2+52+64 = 118 g/mol")],
]
return section_table("🧮 MOLE & MOLAR MASS (Qs 19–26)", rows,
[col_w*0.32, col_w*0.68], hdr_bg=colors.HexColor("#065f46"))
# ═══════════════════════════════════════════════════════
# SECTION 7 — Reactions & Stoichiometry (left)
# ═══════════════════════════════════════════════════════
def rxn_block():
rows = [
[P("<b>Balancing</b>"), P("SiO₂+4HF→SiF₄+2H₂O (HF coeff=4)")],
[P("<b>Precipitate</b>"), P("Solid (s) product from 2 aq solutions, e.g. AgCl(s)")],
[P("<b>Net ionic</b>"), P("Remove spectator ions | H⁺+OH⁻→H₂O")],
[P("<b>Neutralization</b>"),P("Acid+Base→Salt+Water | Ba(OH)₂+2HCl→BaCl₂+2H₂O")],
[P("<b>Stoich steps</b>"), P("1)Balance eq 2)→mol 3)molar ratio 4)→units")],
[P("<b>Limiting R.</b>"), P("Runs out first → determines max product")],
[P("<b>% yield</b>"), P("actual÷theoretical×100 (always ≤100%)")],
[P("<b>Molar ratio</b>"), P("C₃H₈+5O₂→3CO₂+4H₂O : O₂:CO₂ = 5:3")],
]
return section_table("⚗ REACTIONS & STOICHIOMETRY (Qs 11–31)", rows,
[col_w*0.28, col_w*0.72], hdr_bg=colors.HexColor("#9f1239"))
# ═══════════════════════════════════════════════════════
# SECTION 8 — Thermo + Kinetics + Equilibrium (right)
# ═══════════════════════════════════════════════════════
def thermo_block():
rows = [
[P("<b>q=mcΔT ex.</b>"), P("30g Al, 20°C rise: 30×0.9×20=540 J")],
[P("<b>Always spontaneous</b>"),P("ΔH<0 AND ΔS>0 → ΔG always negative")],
[P("<b>Collision theory</b>"), P("Must collide + enough energy + right orientation")],
[P("<b>Activated complex</b>"), P("Unstable high-energy intermediate at energy max")],
[P("<b>↑Temp</b>"), P("Rate ↑ (faster particles, more energetic collisions)")],
[P("<b>Catalyst</b>"), P("Rate ↑, lowers Ea, does NOT change ΔH")],
[P("<b>Equilibrium</b>"), P("Fwd rate = Rev rate; both still occurring")],
[P("<b>Keq</b>"), P("Valid only at ONE temperature; conc/pressure shift = no Keq change")],
[P("<b>Le Chatelier</b>"), P("Stress → shifts to relieve it (add reactant → more product)")],
]
return section_table("🌡 THERMO · KINETICS · EQUILIBRIUM (Qs 1–9)", rows,
[col_w*0.28, col_w*0.72], hdr_bg=colors.HexColor("#1e40af"))
# ═══════════════════════════════════════════════════════
# TITLE BANNER
# ═══════════════════════════════════════════════════════
title_data = [[Paragraph(
"<b>HONORS CHEMISTRY — FINAL EXAM CHEAT SHEET</b> · All 9 Topics · Pendleton High School",
ParagraphStyle("tb", fontSize=9, textColor=WHITE, fontName="Helvetica-Bold",
alignment=TA_CENTER, leading=11)
)]]
title_t = Table(title_data, colWidths=[usable_w])
title_t.setStyle(TableStyle([
("BACKGROUND", (0,0),(0,0), DARK_BLUE),
("TOPPADDING", (0,0),(0,0), 3),
("BOTTOMPADDING",(0,0),(0,0),3),
("LEFTPADDING",(0,0),(0,0),4),
("RIGHTPADDING",(0,0),(0,0),4),
("ROUNDEDCORNERS",[3]),
]))
# ═══════════════════════════════════════════════════════
# Assemble document
# ═══════════════════════════════════════════════════════
story = []
story.append(title_t)
story.append(Spacer(1, 1.5*mm))
# Row 1: Formulas | Polyatomic Ions
story.append(two_col(formulas_block(), ions_block()))
story.append(Spacer(1, 1.5*mm))
# Row 2: Bonding | Nomenclature
story.append(two_col(bonding_block(), nomenclature_block()))
story.append(Spacer(1, 1.5*mm))
# Row 3: Atomic Structure | Mole
story.append(two_col(atomic_block(), mole_block()))
story.append(Spacer(1, 1.5*mm))
# Row 4: Reactions & Stoich | Thermo+Kinetics+Equil
story.append(two_col(rxn_block(), thermo_block()))
doc.build(story)
print(f"✅ Saved to {output_path}")
cd /home/daytona/workspace/chem-cheatsheet && python3 make_cheatsheet.py 2>&1
ls -lh /home/daytona/workspace/chem-cheatsheet/
HonorsChem CheatSheet
PDF Document · PDF
| Priority | Topic | Questions on test |
|---|---|---|
| 🔴 1st | Atomic Structure & Light | Qs 44–50 (7 Qs) |
| 🔴 2nd | Mole & Molar Mass | Qs 19–26 (8 Qs) |
| 🟠 3rd | Stoichiometry | Qs 11–18 (8 Qs) |
| 🟠 4th | Chemical Bonding | Qs 32–40 (9 Qs) |
| 🟡 5th | Nomenclature | Qs 27, 35, 36, 41–43 |
| 🟡 6th | Reactions & Equations | Qs 28–31 |
| 🟢 7th | Thermochemistry | Qs 7–9 |
| 🟢 8th | Kinetics | Qs 3–6 |
| 🟢 9th | Equilibrium | Qs 1–2 |
ok, I have A4 size paper and I have half front cover with periodic table. I want complete cheat sheet that tell me what to do with problem. how to answer questions not just give formula. also I want to do metallic, nonmetallic, charges, etc key on periodic table so take time and make a perfect cheet sheet with ability of pass any student who even doesnot belong to classs
cd /home/daytona/workspace/chem-cheatsheet && python3 -c " from reportlab.lib.pagesizes import A4 print('A4:', A4) # A4 = 595.27 x 841.89 points # Half A4 height = 420.94 points # Full A4 = both pages "
~/chem-cheatsheet/cheatsheet_v2.py
"""
Honors Chemistry – Ultimate Cheat Sheet (2 pages, A4)
Page 1: Periodic Table with full legend
Page 2: Step-by-step problem-solving guide for all 9 exam topics
"""
from reportlab.lib.pagesizes import A4
from reportlab.lib.units import cm, mm, inch
from reportlab.lib import colors
from reportlab.pdfgen import canvas
from reportlab.platypus import (SimpleDocTemplate, Table, TableStyle,
Paragraph, Spacer, HRFlowable, KeepTogether, PageBreak)
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_RIGHT
import math
W, H = A4 # 595.28 x 841.89
OUT = "/home/daytona/workspace/chem-cheatsheet/HonorsChem_UltimateCheatSheet.pdf"
# ═══════════════════════════════════════════════════════════════════
# COLOUR PALETTE
# ═══════════════════════════════════════════════════════════════════
C = {
# element categories
"alkali": colors.HexColor("#ff8a65"), # orange-red
"alkaline": colors.HexColor("#ffcc02"), # yellow
"transition": colors.HexColor("#90caf9"), # light blue
"post_metal": colors.HexColor("#a5d6a7"), # green
"metalloid": colors.HexColor("#ce93d8"), # purple
"nonmetal": colors.HexColor("#fff176"), # bright yellow
"halogen": colors.HexColor("#80deea"), # teal
"noble": colors.HexColor("#ef9a9a"), # pink
"lanthanide": colors.HexColor("#bcaaa4"), # brown-grey
"actinide": colors.HexColor("#b0bec5"), # blue-grey
"unknown": colors.HexColor("#eeeeee"),
# ui
"hdr": colors.HexColor("#1a237e"),
"hdr2": colors.HexColor("#283593"),
"hdr3": colors.HexColor("#1565c0"),
"sec1": colors.HexColor("#0d47a1"),
"sec2": colors.HexColor("#1b5e20"),
"sec3": colors.HexColor("#4a148c"),
"sec4": colors.HexColor("#b71c1c"),
"sec5": colors.HexColor("#e65100"),
"sec6": colors.HexColor("#006064"),
"sec7": colors.HexColor("#880e4f"),
"sec8": colors.HexColor("#33691e"),
"sec9": colors.HexColor("#37474f"),
"step_bg": colors.HexColor("#e8f5e9"),
"warn_bg": colors.HexColor("#fff3e0"),
"tip_bg": colors.HexColor("#e3f2fd"),
"example_bg": colors.HexColor("#fce4ec"),
"white": colors.white,
"light": colors.HexColor("#f5f5f5"),
"border": colors.HexColor("#90a4ae"),
"black": colors.black,
"darktext": colors.HexColor("#212121"),
}
# ═══════════════════════════════════════════════════════════════════
# PERIODIC TABLE DATA
# format: (symbol, atomic_num, category, common_charge)
# category keys match C dict above
# ═══════════════════════════════════════════════════════════════════
ELEMENTS = {
# period 1
(1,1): ("H", 1, "nonmetal", "1+/1−"),
(1,18): ("He", 2, "noble", "0"),
# period 2
(2,1): ("Li", 3, "alkali", "1+"),
(2,2): ("Be", 4, "alkaline", "2+"),
(2,13): ("B", 5, "metalloid", "3+"),
(2,14): ("C", 6, "nonmetal", "4−"),
(2,15): ("N", 7, "nonmetal", "3−"),
(2,16): ("O", 8, "nonmetal", "2−"),
(2,17): ("F", 9, "halogen", "1−"),
(2,18): ("Ne", 10, "noble", "0"),
# period 3
(3,1): ("Na", 11, "alkali", "1+"),
(3,2): ("Mg", 12, "alkaline", "2+"),
(3,13): ("Al", 13, "post_metal","3+"),
(3,14): ("Si", 14, "metalloid", "4−"),
(3,15): ("P", 15, "nonmetal", "3−"),
(3,16): ("S", 16, "nonmetal", "2−"),
(3,17): ("Cl", 17, "halogen", "1−"),
(3,18): ("Ar", 18, "noble", "0"),
# period 4
(4,1): ("K", 19, "alkali", "1+"),
(4,2): ("Ca", 20, "alkaline", "2+"),
(4,3): ("Sc", 21, "transition","3+"),
(4,4): ("Ti", 22, "transition","4+"),
(4,5): ("V", 23, "transition","var"),
(4,6): ("Cr", 24, "transition","var"),
(4,7): ("Mn", 25, "transition","var"),
(4,8): ("Fe", 26, "transition","var"),
(4,9): ("Co", 27, "transition","var"),
(4,10): ("Ni", 28, "transition","var"),
(4,11): ("Cu", 29, "transition","var"),
(4,12): ("Zn", 30, "transition","2+"),
(4,13): ("Ga", 31, "post_metal","3+"),
(4,14): ("Ge", 32, "metalloid", "4−"),
(4,15): ("As", 33, "metalloid", "3−"),
(4,16): ("Se", 34, "nonmetal", "2−"),
(4,17): ("Br", 35, "halogen", "1−"),
(4,18): ("Kr", 36, "noble", "0"),
# period 5
(5,1): ("Rb", 37, "alkali", "1+"),
(5,2): ("Sr", 38, "alkaline", "2+"),
(5,3): ("Y", 39, "transition","3+"),
(5,4): ("Zr", 40, "transition","4+"),
(5,5): ("Nb", 41, "transition","var"),
(5,6): ("Mo", 42, "transition","var"),
(5,7): ("Tc", 43, "transition","var"),
(5,8): ("Ru", 44, "transition","var"),
(5,9): ("Rh", 45, "transition","var"),
(5,10): ("Pd", 46, "transition","var"),
(5,11): ("Ag", 47, "transition","1+"),
(5,12): ("Cd", 48, "transition","2+"),
(5,13): ("In", 49, "post_metal","3+"),
(5,14): ("Sn", 50, "post_metal","var"),
(5,15): ("Sb", 51, "metalloid", "3−"),
(5,16): ("Te", 52, "metalloid", "2−"),
(5,17): ("I", 53, "halogen", "1−"),
(5,18): ("Xe", 54, "noble", "0"),
# period 6
(6,1): ("Cs", 55, "alkali", "1+"),
(6,2): ("Ba", 56, "alkaline", "2+"),
(6,3): ("La*",57, "lanthanide","3+"),
(6,4): ("Hf", 72, "transition","4+"),
(6,5): ("Ta", 73, "transition","5+"),
(6,6): ("W", 74, "transition","var"),
(6,7): ("Re", 75, "transition","var"),
(6,8): ("Os", 76, "transition","var"),
(6,9): ("Ir", 77, "transition","var"),
(6,10): ("Pt", 78, "transition","var"),
(6,11): ("Au", 79, "transition","var"),
(6,12): ("Hg", 80, "transition","var"),
(6,13): ("Tl", 81, "post_metal","var"),
(6,14): ("Pb", 82, "post_metal","var"),
(6,15): ("Bi", 83, "post_metal","3+"),
(6,16): ("Po", 84, "metalloid", "2−"),
(6,17): ("At", 85, "halogen", "1−"),
(6,18): ("Rn", 86, "noble", "0"),
# period 7
(7,1): ("Fr", 87, "alkali", "1+"),
(7,2): ("Ra", 88, "alkaline", "2+"),
(7,3): ("Ac*",89, "actinide", "3+"),
}
# ═══════════════════════════════════════════════════════════════════
# DRAW PERIODIC TABLE (Page 1)
# ═══════════════════════════════════════════════════════════════════
def draw_page1(c_obj):
c_obj.setPageSize(A4)
page_w, page_h = A4
margin = 6*mm
# ── TITLE BAR ──────────────────────────────────────────────────
bar_h = 18
c_obj.setFillColor(C["hdr"])
c_obj.rect(0, page_h - bar_h, page_w, bar_h, fill=1, stroke=0)
c_obj.setFillColor(C["white"])
c_obj.setFont("Helvetica-Bold", 10)
c_obj.drawCentredString(page_w/2, page_h - 13, "HONORS CHEMISTRY — ULTIMATE CHEAT SHEET · PAGE 1: PERIODIC TABLE REFERENCE")
# ── Table area ─────────────────────────────────────────────────
top = page_h - bar_h - 3*mm
table_area_h = page_h * 0.50 # upper half
legend_area_h = page_h * 0.47 # lower half
# How many rows/cols: 7 rows (periods) + 2 gap rows for lanthanides/actinides
# 18 columns (groups)
num_rows = 9 # 7 periods + blank + 2 lan/act rows
num_cols = 18
cell_w = (page_w - 2*margin) / num_cols
cell_h = table_area_h / (num_rows + 0.5)
def draw_element(period, group, x_off=0, y_off=0):
key = (period, group)
if key not in ELEMENTS:
return
sym, anum, cat, charge = ELEMENTS[key]
# position: col = group-1, row = period-1
col_idx = group - 1
row_idx = period - 1
x = margin + col_idx * cell_w + x_off
y = top - (row_idx + 1) * cell_h + y_off
bg = C.get(cat, C["unknown"])
c_obj.setFillColor(bg)
c_obj.setStrokeColor(colors.HexColor("#546e7a"))
c_obj.setLineWidth(0.3)
c_obj.rect(x, y, cell_w - 0.5, cell_h - 0.5, fill=1, stroke=1)
# atomic number
c_obj.setFillColor(colors.HexColor("#37474f"))
c_obj.setFont("Helvetica", 4.5)
c_obj.drawString(x + 1, y + cell_h - 6, str(anum))
# symbol
c_obj.setFillColor(C["black"])
sym_size = 7.5 if len(sym) <= 2 else 6.5
c_obj.setFont("Helvetica-Bold", sym_size)
c_obj.drawCentredString(x + cell_w/2, y + cell_h/2 - 2, sym)
# charge (tiny)
c_obj.setFillColor(colors.HexColor("#b71c1c"))
c_obj.setFont("Helvetica", 3.8)
c_obj.drawCentredString(x + cell_w/2, y + 1.5, charge)
# Draw all elements periods 1-7
for (period, group) in ELEMENTS:
if period <= 7:
draw_element(period, group)
# ── Lanthanide / Actinide rows (offset below main table) ───────
lan_y_offset = -(1.3 * cell_h) # shift down 1.3 rows below row 7
act_y_offset = -(2.0 * cell_h)
lan_symbols = [("La",57,"lanthanide","3+"),("Ce",58,"lanthanide","3+"),
("Pr",59,"lanthanide","3+"),("Nd",60,"lanthanide","3+"),
("Pm",61,"lanthanide","var"),("Sm",62,"lanthanide","3+"),
("Eu",63,"lanthanide","3+"),("Gd",64,"lanthanide","3+"),
("Tb",65,"lanthanide","3+"),("Dy",66,"lanthanide","3+"),
("Ho",67,"lanthanide","3+"),("Er",68,"lanthanide","3+"),
("Tm",69,"lanthanide","3+"),("Yb",70,"lanthanide","2+/3+"),
("Lu",71,"lanthanide","3+")]
act_symbols = [("Ac",89,"actinide","3+"),("Th",90,"actinide","4+"),
("Pa",91,"actinide","5+"),("U",92,"actinide","var"),
("Np",93,"actinide","var"),("Pu",94,"actinide","var"),
("Am",95,"actinide","3+"),("Cm",96,"actinide","3+"),
("Bk",97,"actinide","3+"),("Cf",98,"actinide","3+"),
("Es",99,"actinide","3+"),("Fm",100,"actinide","3+"),
("Md",101,"actinide","3+"),("No",102,"actinide","2+"),
("Lr",103,"actinide","3+")]
for i, (sym, anum, cat, charge) in enumerate(lan_symbols):
col_idx = i + 3 # start at column 4
row_idx = 7 # row 8 (below period 7)
x = margin + col_idx * cell_w
y = top - (row_idx + 1) * cell_h + lan_y_offset
bg = C.get(cat, C["unknown"])
c_obj.setFillColor(bg)
c_obj.setStrokeColor(colors.HexColor("#546e7a"))
c_obj.setLineWidth(0.3)
c_obj.rect(x, y, cell_w - 0.5, cell_h - 0.5, fill=1, stroke=1)
c_obj.setFillColor(colors.HexColor("#37474f"))
c_obj.setFont("Helvetica", 4.5)
c_obj.drawString(x + 1, y + cell_h - 6, str(anum))
c_obj.setFillColor(C["black"])
c_obj.setFont("Helvetica-Bold", 7)
c_obj.drawCentredString(x + cell_w/2, y + cell_h/2 - 2, sym)
c_obj.setFillColor(colors.HexColor("#b71c1c"))
c_obj.setFont("Helvetica", 3.8)
c_obj.drawCentredString(x + cell_w/2, y + 1.5, charge)
for i, (sym, anum, cat, charge) in enumerate(act_symbols):
col_idx = i + 3
row_idx = 8
x = margin + col_idx * cell_w
y = top - (row_idx + 1) * cell_h + act_y_offset
bg = C.get(cat, C["unknown"])
c_obj.setFillColor(bg)
c_obj.setStrokeColor(colors.HexColor("#546e7a"))
c_obj.setLineWidth(0.3)
c_obj.rect(x, y, cell_w - 0.5, cell_h - 0.5, fill=1, stroke=1)
c_obj.setFillColor(colors.HexColor("#37474f"))
c_obj.setFont("Helvetica", 4.5)
c_obj.drawString(x + 1, y + cell_h - 6, str(anum))
c_obj.setFillColor(C["black"])
c_obj.setFont("Helvetica-Bold", 7)
c_obj.drawCentredString(x + cell_w/2, y + cell_h/2 - 2, sym)
c_obj.setFillColor(colors.HexColor("#b71c1c"))
c_obj.setFont("Helvetica", 3.8)
c_obj.drawCentredString(x + cell_w/2, y + 1.5, charge)
# ── Group labels across top ─────────────────────────────────────
groups = ["1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18"]
group_names = ["IA","IIA","","","","","","","","","","","IIIA","IVA","VA","VIA","VIIA","0"]
for i, (g, gn) in enumerate(zip(groups, group_names)):
x = margin + i * cell_w + cell_w/2
y = top + 1*mm
c_obj.setFillColor(C["hdr2"])
c_obj.setFont("Helvetica-Bold", 5.5)
c_obj.drawCentredString(x, y, g)
c_obj.setFillColor(C["sec9"])
c_obj.setFont("Helvetica", 4.5)
c_obj.drawCentredString(x, y - 5, gn)
# ── Period labels ───────────────────────────────────────────────
for p in range(1, 8):
y = top - p * cell_h + cell_h/2 - 2
c_obj.setFillColor(C["hdr2"])
c_obj.setFont("Helvetica-Bold", 6)
c_obj.drawRightString(margin - 1, y, str(p))
# ═══════════════════════════════════════════════════════════════
# LEGEND AREA (lower half of page)
# ═══════════════════════════════════════════════════════════════
# Calculate where periodic table ends
last_element_y = top - 7 * cell_h + act_y_offset - 2*mm
legend_top = last_element_y - 4*mm
# ── Legend title ───────────────────────────────────────────────
c_obj.setFillColor(C["hdr"])
c_obj.rect(margin, legend_top - 10, page_w - 2*margin, 11, fill=1, stroke=0)
c_obj.setFillColor(C["white"])
c_obj.setFont("Helvetica-Bold", 8)
c_obj.drawCentredString(page_w/2, legend_top - 7, "PERIODIC TABLE KEY — Everything you need to read and use the table above")
legend_y = legend_top - 14
# ── Category legend ────────────────────────────────────────────
cats = [
("Alkali Metals\n(Group 1)", "alkali", "Very reactive, 1+ charge\nNa, K, Li, Rb, Cs"),
("Alkaline Earth\n(Group 2)", "alkaline", "Reactive, 2+ charge\nMg, Ca, Ba, Sr"),
("Transition Metals\n(Groups 3–12)", "transition", "Often variable charge\nFe, Cu, Zn, Cr, Mn"),
("Post-Transition\nMetals", "post_metal", "Lower melting metals\nAl(3+), Pb, Sn"),
("Metalloids", "metalloid", "Semi-metal properties\nSi, Ge, As, B"),
("Nonmetals\n(non-halogens)", "nonmetal", "Form negative ions\nC, N, O, P, S"),
("Halogens\n(Group 17)", "halogen", "Very reactive, 1− charge\nF, Cl, Br, I"),
("Noble Gases\n(Group 18)", "noble", "Inert, 0 charge\nHe, Ne, Ar"),
]
box_w = (page_w - 2*margin) / 4
box_h = 22
for i, (name, cat, desc) in enumerate(cats):
col = i % 4
row = i // 4
x = margin + col * box_w
y = legend_y - row * (box_h + 1.5)
# color swatch
c_obj.setFillColor(C[cat])
c_obj.setStrokeColor(C["border"])
c_obj.setLineWidth(0.5)
c_obj.rect(x, y - box_h + 2, box_w - 2, box_h - 2, fill=1, stroke=1)
c_obj.setFillColor(C["black"])
c_obj.setFont("Helvetica-Bold", 6.5)
# split name lines
lines = name.split("\n")
for li, ln in enumerate(lines):
c_obj.drawString(x + 2, y - 8 - li*7, ln)
c_obj.setFillColor(colors.HexColor("#37474f"))
c_obj.setFont("Helvetica", 5.5)
desc_lines = desc.split("\n")
for li, ln in enumerate(desc_lines):
c_obj.drawString(x + 2, y - 8 - len(lines)*7 - li*6, ln)
legend_y2 = legend_y - 2*(box_h + 1.5) - 5*mm
# ── Metal vs Nonmetal dividing line arrow ──────────────────────
c_obj.setFillColor(C["hdr"])
c_obj.setFont("Helvetica-Bold", 7)
c_obj.drawString(margin, legend_y2, "METALS (left of staircase) vs NONMETALS (right of staircase) vs METALLOIDS (on the staircase line)")
# ── Group charges quick-ref ────────────────────────────────────
legend_y3 = legend_y2 - 7*mm
c_obj.setFillColor(C["hdr2"])
c_obj.rect(margin, legend_y3 - 10, page_w - 2*margin, 11, fill=1, stroke=0)
c_obj.setFillColor(C["white"])
c_obj.setFont("Helvetica-Bold", 7.5)
c_obj.drawCentredString(page_w/2, legend_y3 - 7.5, "COMMON ION CHARGES — Memorize these!")
charges_table_y = legend_y3 - 14
charge_data = [
("Group 1\n(Li,Na,K,Rb,Cs)", "1+", C["alkali"]),
("Group 2\n(Be,Mg,Ca,Sr,Ba)", "2+", C["alkaline"]),
("Group 13\n(Al,Ga,In)", "3+", C["post_metal"]),
("Group 15\n(N,P,As)", "3−", C["nonmetal"]),
("Group 16\n(O,S,Se,Te)", "2−", C["nonmetal"]),
("Group 17\n(F,Cl,Br,I,At)", "1−", C["halogen"]),
("Group 18\n(He,Ne,Ar,Kr)", "0", C["noble"]),
("Transition metals\n(Fe,Cu,Cr,Mn…)", "VARIABLE\n(Roman #)", C["transition"]),
]
cbox_w = (page_w - 2*margin) / 8
cbox_h = 22
for i, (name, charge, bg) in enumerate(charge_data):
x = margin + i * cbox_w
y_c = charges_table_y
c_obj.setFillColor(bg)
c_obj.setStrokeColor(C["border"])
c_obj.setLineWidth(0.5)
c_obj.rect(x, y_c - cbox_h, cbox_w - 1, cbox_h, fill=1, stroke=1)
c_obj.setFillColor(C["black"])
c_obj.setFont("Helvetica", 5.5)
name_lines = name.split("\n")
for li, ln in enumerate(name_lines):
c_obj.drawCentredString(x + cbox_w/2, y_c - 8 - li*6, ln)
c_obj.setFillColor(C["hdr"])
c_obj.setFont("Helvetica-Bold", 9)
c_obj.drawCentredString(x + cbox_w/2, y_c - cbox_h + 3, charge)
# ── Special elements box ───────────────────────────────────────
spec_y = charges_table_y - cbox_h - 5*mm
# box background
c_obj.setFillColor(colors.HexColor("#e3f2fd"))
c_obj.setStrokeColor(C["hdr3"])
c_obj.setLineWidth(0.6)
col_w2 = (page_w - 2*margin) / 3
box_height = 30
c_obj.rect(margin, spec_y - box_height, page_w - 2*margin, box_height, fill=1, stroke=1)
# Three special notes side by side
specials = [
("★ Diatomic Elements (HOFBrINCl)",
"These 7 exist as PAIRS in nature:\nH₂ O₂ F₂ Br₂ I₂ N₂ Cl₂\nAlways write them with subscript 2!"),
("★ Noble Gases = MONOATOMIC",
"He, Ne, Ar, Kr, Xe, Rn\nGo solo — never bond with others.\nDo NOT write He₂!"),
("★ Variable-Charge Metals (use Roman #)",
"Fe: 2+ (iron II) or 3+ (iron III)\nCu: 1+ (copper I) or 2+ (copper II)\nPb: 2+ or 4+ Sn: 2+ or 4+"),
]
for i, (title, body) in enumerate(specials):
x = margin + i * col_w2 + 2
c_obj.setFillColor(C["hdr"])
c_obj.setFont("Helvetica-Bold", 6.5)
c_obj.drawString(x, spec_y - 9, title)
c_obj.setFillColor(C["darktext"])
c_obj.setFont("Helvetica", 5.8)
for li, ln in enumerate(body.split("\n")):
c_obj.drawString(x, spec_y - 17 - li*6.5, ln)
# ── Polyatomic ions ─────────────────────────────────────────────
ions_y = spec_y - box_height - 4*mm
c_obj.setFillColor(colors.HexColor("#4a148c"))
c_obj.rect(margin, ions_y - 10, page_w - 2*margin, 11, fill=1, stroke=0)
c_obj.setFillColor(C["white"])
c_obj.setFont("Helvetica-Bold", 7.5)
c_obj.drawCentredString(page_w/2, ions_y - 7.5, "POLYATOMIC IONS YOU MUST KNOW (these appear on the exam!)")
ion_y2 = ions_y - 14
ions = [
("SO₄²⁻", "sulfate"), ("SO₃²⁻", "sulfite"),
("NO₃⁻", "nitrate"), ("NO₂⁻", "nitrite"),
("PO₄³⁻", "phosphate"), ("OH⁻", "hydroxide"),
("NH₄⁺", "ammonium"), ("ClO⁻", "hypochlorite"),
("ClO₂⁻", "chlorite"), ("ClO₃⁻", "chlorate"),
("ClO₄⁻", "perchlorate"),("CO₃²⁻","carbonate"),
]
ion_col_w = (page_w - 2*margin) / 6
for i, (ion, name) in enumerate(ions):
col = i % 6
row = i // 6
x = margin + col * ion_col_w
y_i = ion_y2 - row * 11
bg_color = colors.HexColor("#ede7f6") if i % 2 == 0 else colors.HexColor("#f3e5f5")
c_obj.setFillColor(bg_color)
c_obj.setStrokeColor(colors.HexColor("#7b1fa2"))
c_obj.setLineWidth(0.4)
c_obj.rect(x, y_i - 9, ion_col_w - 1, 10, fill=1, stroke=1)
c_obj.setFillColor(colors.HexColor("#4a148c"))
c_obj.setFont("Helvetica-Bold", 7)
c_obj.drawString(x + 2, y_i - 7, ion)
c_obj.setFillColor(C["darktext"])
c_obj.setFont("Helvetica", 6)
c_obj.drawString(x + 30, y_i - 7, name)
# ── Memory tricks ───────────────────────────────────────────────
mem_y = ion_y2 - 2 * 11 - 3*mm
c_obj.setFillColor(colors.HexColor("#fff8e1"))
c_obj.setStrokeColor(colors.HexColor("#f57f17"))
c_obj.setLineWidth(0.7)
c_obj.rect(margin, mem_y - 24, page_w - 2*margin, 25, fill=1, stroke=1)
c_obj.setFillColor(colors.HexColor("#e65100"))
c_obj.setFont("Helvetica-Bold", 7)
c_obj.drawString(margin + 3, mem_y - 8, "MEMORY TRICKS:")
c_obj.setFillColor(C["darktext"])
c_obj.setFont("Helvetica", 6.5)
tricks = (
" -ate = MORE oxygens | -ite = FEWER oxygens (sulfATE=SO₄²⁻ has MORE O than sulfITE=SO₃²⁻) "
"| hypo- = fewest O (ClO⁻) | per- = most O (ClO₄⁻) "
"| Electron config fill order: 1s 2s 2p 3s 3p 4s 3d 4p (4s BEFORE 3d!)"
)
c_obj.drawString(margin + 3, mem_y - 18, tricks[:110])
c_obj.drawString(margin + 3, mem_y - 26 + 2, tricks[110:])
# ── Footer ──────────────────────────────────────────────────────
c_obj.setFillColor(C["hdr"])
c_obj.rect(0, 0, page_w, 10, fill=1, stroke=0)
c_obj.setFillColor(C["white"])
c_obj.setFont("Helvetica", 5.5)
c_obj.drawCentredString(page_w/2, 3, "Page 1 of 2 · Pendleton High School · Honors Chemistry Final Exam · Flip for STEP-BY-STEP PROBLEM SOLVING GUIDE →")
# ═══════════════════════════════════════════════════════════════════
# PAGE 2 — STEP-BY-STEP PROBLEM SOLVING GUIDE
# ═══════════════════════════════════════════════════════════════════
def draw_page2(c_obj):
c_obj.showPage()
c_obj.setPageSize(A4)
page_w, page_h = A4
margin = 6*mm
# TITLE BAR
c_obj.setFillColor(C["hdr"])
c_obj.rect(0, page_h - 18, page_w, 18, fill=1, stroke=0)
c_obj.setFillColor(C["white"])
c_obj.setFont("Helvetica-Bold", 10)
c_obj.drawCentredString(page_w/2, page_h - 13, "PAGE 2: HOW TO SOLVE EVERY PROBLEM TYPE · Step-by-step for ALL 9 exam topics")
y = page_h - 22
col_w = (page_w - 2*margin - 3*mm) / 2
left_x = margin
right_x = margin + col_w + 3*mm
# ─────────────────────────────────────────────────────────────
# Helper functions
# ─────────────────────────────────────────────────────────────
def section_bar(x, y, w, title, color):
c_obj.setFillColor(color)
c_obj.rect(x, y - 11, w, 11, fill=1, stroke=0)
c_obj.setFillColor(C["white"])
c_obj.setFont("Helvetica-Bold", 7.5)
c_obj.drawString(x + 3, y - 8.5, title)
return y - 12
def step_box(x, y, w, steps, bg=colors.HexColor("#e8f5e9"), border=colors.HexColor("#2e7d32")):
"""Draw numbered step box, returns new y"""
line_h = 8
padding = 3
total_h = len(steps) * line_h + 2 * padding
c_obj.setFillColor(bg)
c_obj.setStrokeColor(border)
c_obj.setLineWidth(0.5)
c_obj.rect(x, y - total_h, w, total_h, fill=1, stroke=1)
for i, step in enumerate(steps):
c_obj.setFillColor(border)
c_obj.setFont("Helvetica-Bold", 6)
c_obj.drawString(x + 2, y - padding - i*line_h - 5.5, f"{i+1}.")
c_obj.setFillColor(C["darktext"])
c_obj.setFont("Helvetica", 6)
c_obj.drawString(x + 10, y - padding - i*line_h - 5.5, step)
return y - total_h - 1
def example_box(x, y, w, text, bg=colors.HexColor("#fce4ec"), border=colors.HexColor("#c62828")):
lines = text.split("\n")
line_h = 7.5
padding = 3
total_h = len(lines) * line_h + 2 * padding
c_obj.setFillColor(bg)
c_obj.setStrokeColor(border)
c_obj.setLineWidth(0.5)
c_obj.rect(x, y - total_h, w, total_h, fill=1, stroke=1)
c_obj.setFillColor(border)
c_obj.setFont("Helvetica-Bold", 5.5)
c_obj.drawString(x + 2, y - padding - 5, "EXAMPLE:")
c_obj.setFillColor(C["darktext"])
c_obj.setFont("Helvetica", 5.8)
for i, ln in enumerate(lines):
c_obj.drawString(x + 2, y - padding - (i+1)*line_h - 2, ln)
return y - total_h - 1
def tip_box(x, y, w, text, bg=colors.HexColor("#e3f2fd"), border=colors.HexColor("#1565c0")):
lines = text.split("\n")
line_h = 7
padding = 2.5
total_h = len(lines) * line_h + 2 * padding
c_obj.setFillColor(bg)
c_obj.setStrokeColor(border)
c_obj.setLineWidth(0.4)
c_obj.rect(x, y - total_h, w, total_h, fill=1, stroke=1)
c_obj.setFillColor(border)
c_obj.setFont("Helvetica-Bold", 5.5)
c_obj.drawString(x + 2, y - padding - 5, "★ TIP:")
c_obj.setFillColor(C["darktext"])
c_obj.setFont("Helvetica", 5.8)
for i, ln in enumerate(lines):
c_obj.drawString(x + 30, y - padding - i*line_h - 5, ln)
return y - total_h - 1
# ═══════════════════════════════════════
# LEFT COLUMN
# ═══════════════════════════════════════
ly = y - 2
# 1. LIGHT & PHOTON ENERGY (Qs 44-50)
ly = section_bar(left_x, ly, col_w, "1. LIGHT & PHOTON ENERGY (Qs 44–50)", C["sec3"])
ly = step_box(left_x, ly, col_w, [
"Find wavelength (λ) or frequency (ν)? Use: ν = c / λ",
"Find energy (E)? Use: E = h × ν",
"c = 3.00×10⁸ m/s h = 6.626×10⁻³⁴ J·s",
"Watch units! λ must be in METERS (convert nm: ×10⁻⁹)",
])
ly = example_box(left_x, ly, col_w,
"λ = 500 nm = 5.00×10⁻⁷ m\nν = 3.00×10⁸ / 5.00×10⁻⁷ = 6.00×10¹⁴ Hz\nE = 6.626×10⁻³⁴ × 6.00×10¹⁴ = 3.98×10⁻¹⁹ J")
ly -= 1.5*mm
# 2. ELECTRON CONFIGURATION (Qs 44-50)
ly = section_bar(left_x, ly, col_w, "2. ELECTRON CONFIGURATION (Qs 44–50)", C["sec3"])
ly = step_box(left_x, ly, col_w, [
"Fill in ORDER: 1s² 2s² 2p⁶ 3s² 3p⁶ 4s² 3d¹⁰ 4p⁶ ...",
"USE [noble gas] shortcut: find previous noble gas",
"Then continue filling from where it left off",
"Check: total electrons = atomic number (neutral atom)",
])
ly = example_box(left_x, ly, col_w,
"Germanium (Ge, Z=32, Period 4 Group 14)\nPrev. noble gas = Ar (Z=18) → [Ar]\nThen add: 4s² 3d¹⁰ 4p² (32-18=14 more e⁻)\nFull config: [Ar] 4s² 3d¹⁰ 4p²")
ly -= 1.5*mm
# 3. NAMING IONIC COMPOUNDS
ly = section_bar(left_x, ly, col_w, "3. NAME IONIC COMPOUNDS (Qs 27,41–43)", C["sec5"])
ly = step_box(left_x, ly, col_w, [
"1st: Write metal name as-is",
"2nd: Is it a variable-charge metal? (Fe,Cu,Pb,Sn,Hg,Cr,Mn...)",
" YES → figure out charge, add Roman numeral in ()",
" NO → skip Roman numeral",
"3rd: Write anion name (-ide for simple, -ate/-ite for polyatomic)",
])
ly = example_box(left_x, ly, col_w,
"Fe₂(SO₄)₃ → 3 sulfate = 6− total → Fe must be 3+ each\n→ Iron(III) sulfate\nCa₃(PO₄)₂ → Ca is always 2+, no Roman numeral\n→ Calcium phosphate")
ly -= 1.5*mm
# 4. WRITING IONIC FORMULAS
ly = section_bar(left_x, ly, col_w, "4. WRITE IONIC FORMULAS (Qs 35–36)", C["sec5"])
ly = step_box(left_x, ly, col_w, [
"Write cation (metal, +) first, anion (nonmetal, −) second",
"CROSSOVER: swap the charge numbers as subscripts",
"Reduce if possible (e.g. Ca²⁺ + O²⁻ → CaO, not Ca₂O₂)",
"Polyatomic ion with subscript >1? Put it in parentheses",
])
ly = example_box(left_x, ly, col_w,
"Ca²⁺ + PO₄³⁻ → cross: Ca₃(PO₄)₂\n Check: 3×(2+) = 6+ and 2×(3−) = 6− ✓\nFe³⁺ + O²⁻ → Fe₂O₃ (cross 3 and 2)")
ly -= 1.5*mm
# 5. MOLE CALCULATIONS
ly = section_bar(left_x, ly, col_w, "5. MOLE CALCULATIONS (Qs 19–26)", C["sec2"])
ly = step_box(left_x, ly, col_w, [
"FIND MOLAR MASS FIRST: add atomic masses of all atoms",
"Grams → Moles: moles = grams ÷ molar mass",
"Moles → Grams: grams = moles × molar mass",
"Moles → Particles: particles = moles × 6.02×10²³",
"Particles → Moles: moles = particles ÷ 6.02×10²³",
])
ly = example_box(left_x, ly, col_w,
"How many moles in 100 g Cl₂?\n MM of Cl₂ = 2 × 35.5 = 71.0 g/mol\n moles = 100 ÷ 71.0 = 1.41 mol\nMM of H₂CrO₄ = 2(1) + 52 + 4(16) = 118 g/mol")
ly -= 1.5*mm
# 6. PERCENT COMPOSITION
ly = section_bar(left_x, ly, col_w, "6. PERCENT COMPOSITION (Qs 19–26)", C["sec2"])
ly = step_box(left_x, ly, col_w, [
"Calculate molar mass of the compound",
"For each element: % = (elem. mass in formula / MM) × 100",
"All percentages must add to 100%",
])
ly = example_box(left_x, ly, col_w,
"% C in CO₂? MM=44 g/mol\n % C = 12/44 × 100 = 27.3%\n % O = 32/44 × 100 = 72.7% ✓ (27.3+72.7=100)")
# ═══════════════════════════════════════
# RIGHT COLUMN
# ═══════════════════════════════════════
ry = y - 2
# 7. EMPIRICAL FORMULA
ry = section_bar(right_x, ry, col_w, "7. EMPIRICAL FORMULA FROM % COMP (Qs 19–26)", C["sec2"])
ry = step_box(right_x, ry, col_w, [
"Assume 100 g sample → % directly becomes grams",
"Divide each element's grams by its atomic mass → moles",
"Divide ALL mole values by the SMALLEST one",
"Round to nearest whole number → those are subscripts",
"If .5 remains, multiply all by 2; if .33, multiply by 3",
])
ry = example_box(right_x, ry, col_w,
"85.6% C, 14.4% H\n C: 85.6 ÷ 12 = 7.13 mol\n H: 14.4 ÷ 1 = 14.4 mol\n Divide by smallest (7.13): C=1, H=2.02≈2\n Empirical formula: CH₂")
ry -= 1.5*mm
# 8. STOICHIOMETRY (Qs 11-18)
ry = section_bar(right_x, ry, col_w, "8. STOICHIOMETRY (Qs 11–18)", C["sec4"])
ry = step_box(right_x, ry, col_w, [
"STEP 1: Write and BALANCE the equation (never skip!)",
"STEP 2: Convert what you're given to MOLES",
" (grams ÷ molar mass)",
"STEP 3: Multiply by the MOLAR RATIO from equation",
" (coefficient wanted / coefficient given)",
"STEP 4: Convert moles to what they ask for",
" (× molar mass for grams; × 6.02×10²³ for particles)",
])
ry = example_box(right_x, ry, col_w,
"C₃H₈ + 5O₂ → 3CO₂ + 4H₂O\nHow many mol CO₂ from 2 mol C₃H₈?\n ratio CO₂:C₃H₈ = 3:1\n 2 mol C₃H₈ × (3/1) = 6 mol CO₂")
ry -= 1.5*mm
# 9. LIMITING REACTANT
ry = section_bar(right_x, ry, col_w, "9. LIMITING REACTANT (Qs 11–18)", C["sec4"])
ry = step_box(right_x, ry, col_w, [
"Balance the equation",
"Convert BOTH reactants to moles",
"Use molar ratio → find how much product each reactant makes",
"The one that makes LESS product = LIMITING reactant",
"Use limiting reactant's amount to find theoretical yield",
])
ry = example_box(right_x, ry, col_w,
"5 mol Fe + 4 mol O₂ → 2Fe₂O₃?\n Balanced: 4Fe + 3O₂ → 2Fe₂O₃\n Fe: 5 × (2/4) = 2.5 mol product\n O₂: 4 × (2/3) = 2.67 mol product\n Fe makes less → Fe is LIMITING REACTANT")
ry -= 1.5*mm
# 10. PERCENT YIELD
ry = section_bar(right_x, ry, col_w, "10. PERCENT YIELD (Qs 11–18)", C["sec4"])
ry = step_box(right_x, ry, col_w, [
"Theoretical yield = what stoichiometry PREDICTS (max possible)",
"Actual yield = what you ACTUALLY got in the lab",
"% yield = (actual ÷ theoretical) × 100",
"% yield is ALWAYS ≤ 100% (can't make more than predicted!)",
])
ry = example_box(right_x, ry, col_w,
"Theory says 10 g, lab gives 8.2 g\n% yield = (8.2 / 10) × 100 = 82%")
ry -= 1.5*mm
# 11. THERMOCHEMISTRY
ry = section_bar(right_x, ry, col_w, "11. HEAT & SPONTANEITY (Qs 7–9)", C["sec6"])
ry = step_box(right_x, ry, col_w, [
"HEAT: q = m × c × ΔT (ΔT = T_final − T_initial)",
"c for water = 4.18 J/g·°C c for Al = 0.900 J/g·°C",
"CALORIES: 1 Cal (food) = 1 kcal = 4184 J",
"SPONTANEOUS if: ΔH<0 AND ΔS>0 (always, any temp)",
"Catalyst: lowers Ea, DOES NOT change ΔH",
])
ry = example_box(right_x, ry, col_w,
"30 g Al heated 20°C→40°C:\n q = 30 × 0.900 × (40−20) = 540 J\n170 Calories to J:\n 170 × 4184 = 711,280 J = 7.11×10⁵ J")
ry -= 1.5*mm
# 12. KINETICS + EQUILIBRIUM
ry = section_bar(right_x, ry, col_w, "12. KINETICS & EQUILIBRIUM (Qs 1–6)", C["sec7"])
ry = step_box(right_x, ry, col_w, [
"Rate INCREASES with: ↑temp, ↑conc, ↑surface area, catalyst",
"Activated complex = unstable high-E intermediate (not prod, not react)",
"EQUILIBRIUM: forward rate = reverse rate (both still running!)",
"Keq changes ONLY with temperature",
"Le Chatelier: add reactant → shift right (→ more product)",
"Le Chatelier: add product → shift left (← more reactant)",
])
ry = tip_box(right_x, ry, col_w,
"Conc/pressure changes SHIFT equilibrium but DO NOT change Keq value!\nOnly temperature changes Keq itself.")
# ── BALANCING & NET IONIC (across bottom, full width) ──────────
bottom_y = min(ly, ry) - 3*mm
full_w = page_w - 2*margin
bottom_y = section_bar(left_x, bottom_y, full_w, "13. BALANCING EQUATIONS & NET IONIC (Qs 28–31)", C["sec9"])
# two sub-columns
sub_w = (full_w - 2*mm) / 2
# balance steps
balance_steps = [
"Count atoms of each element on each side",
"Add COEFFICIENTS (big numbers in front) to balance",
"NEVER change subscripts inside formulas",
"Balance in this order: metals first, then nonmetals, H last, O last",
]
for i, s in enumerate(balance_steps):
c_obj.setFillColor(colors.HexColor("#e8f5e9"))
c_obj.setStrokeColor(colors.HexColor("#2e7d32"))
c_obj.setLineWidth(0.3)
# left balance box
bx = left_x
by = bottom_y - 1
bh = len(balance_steps) * 8 + 6
c_obj.setFillColor(colors.HexColor("#e8f5e9"))
c_obj.setStrokeColor(colors.HexColor("#2e7d32"))
c_obj.setLineWidth(0.5)
c_obj.rect(bx, by - bh, sub_w, bh, fill=1, stroke=1)
c_obj.setFillColor(colors.HexColor("#1b5e20"))
c_obj.setFont("Helvetica-Bold", 6)
c_obj.drawString(bx+2, by-7, "HOW TO BALANCE:")
for i, s in enumerate(balance_steps):
c_obj.setFillColor(colors.HexColor("#1b5e20"))
c_obj.setFont("Helvetica-Bold", 5.8)
c_obj.drawString(bx+2, by - 15 - i*8, f"{i+1}.")
c_obj.setFillColor(C["darktext"])
c_obj.setFont("Helvetica", 5.8)
c_obj.drawString(bx+10, by - 15 - i*8, s)
# example
ex_y = by - bh - 1
ex_text = ["EX: SiO₂ + HF → SiF₄ + H₂O",
"Need 4F on right → 4HF on left",
"4HF → 4H → 2H₂O → 2O matches SiO₂ ✓",
"Answer: SiO₂ + 4HF → SiF₄ + 2H₂O"]
ex_h = len(ex_text)*7 + 4
c_obj.setFillColor(colors.HexColor("#fce4ec"))
c_obj.setStrokeColor(colors.HexColor("#c62828"))
c_obj.setLineWidth(0.4)
c_obj.rect(bx, ex_y - ex_h, sub_w, ex_h, fill=1, stroke=1)
for i, ln in enumerate(ex_text):
c_obj.setFillColor(C["darktext"])
c_obj.setFont("Helvetica", 5.8)
c_obj.drawString(bx+3, ex_y - 7 - i*7, ln)
# right: net ionic
nx = left_x + sub_w + 2
ny = bottom_y - 1
net_steps = [
"Write FULL equation with all compounds",
"Split ALL aqueous (aq) ionic compounds into their ions",
"Solids (s), liquids (l), gases (g) stay as molecules",
"Cross out ions that appear UNCHANGED on both sides (spectators)",
"What remains = NET ionic equation",
]
nh = len(net_steps) * 8 + 6
c_obj.setFillColor(colors.HexColor("#e3f2fd"))
c_obj.setStrokeColor(colors.HexColor("#1565c0"))
c_obj.setLineWidth(0.5)
c_obj.rect(nx, ny - nh, sub_w, nh, fill=1, stroke=1)
c_obj.setFillColor(colors.HexColor("#0d47a1"))
c_obj.setFont("Helvetica-Bold", 6)
c_obj.drawString(nx+2, ny-7, "NET IONIC EQUATION:")
for i, s in enumerate(net_steps):
c_obj.setFillColor(colors.HexColor("#0d47a1"))
c_obj.setFont("Helvetica-Bold", 5.8)
c_obj.drawString(nx+2, ny - 15 - i*8, f"{i+1}.")
c_obj.setFillColor(C["darktext"])
c_obj.setFont("Helvetica", 5.8)
c_obj.drawString(nx+10, ny - 15 - i*8, s)
nex_y = ny - nh - 1
nex = ["HCl(aq)+KOH(aq)→KCl(aq)+H₂O(l)",
"Split: H⁺+Cl⁻+K⁺+OH⁻→K⁺+Cl⁻+H₂O",
"Spectators: K⁺ and Cl⁻ (cancel them)",
"Net ionic: H⁺(aq) + OH⁻(aq) → H₂O(l)"]
nex_h = len(nex)*7 + 4
c_obj.setFillColor(colors.HexColor("#fce4ec"))
c_obj.setStrokeColor(colors.HexColor("#c62828"))
c_obj.setLineWidth(0.4)
c_obj.rect(nx, nex_y - nex_h, sub_w, nex_h, fill=1, stroke=1)
for i, ln in enumerate(nex):
c_obj.setFillColor(C["darktext"])
c_obj.setFont("Helvetica", 5.8)
c_obj.drawString(nx+3, nex_y - 7 - i*7, ln)
# FOOTER
c_obj.setFillColor(C["hdr"])
c_obj.rect(0, 0, page_w, 10, fill=1, stroke=0)
c_obj.setFillColor(C["white"])
c_obj.setFont("Helvetica", 5.5)
c_obj.drawCentredString(page_w/2, 3, "Page 2 of 2 · Pendleton High School · Honors Chemistry Final Exam · ← See Page 1 for Periodic Table")
# ═══════════════════════════════════════════════════════════════════
# BUILD PDF
# ═══════════════════════════════════════════════════════════════════
c_obj = canvas.Canvas(OUT, pagesize=A4)
draw_page1(c_obj)
draw_page2(c_obj)
c_obj.save()
print(f"✅ Saved: {OUT}")
cd /home/daytona/workspace/chem-cheatsheet && python3 cheatsheet_v2.py 2>&1
ls -lh /home/daytona/workspace/chem-cheatsheet/HonorsChem_UltimateCheatSheet.pdf
HonorsChem UltimateCheatSheet
PDF Document · PDF
this is good but I want something that has everything from both the study guide you provide means theory and how to do. also give example but I am not putting them on cheat sheet
~/chem-cheatsheet/studyguide_complete.py
"""
Honors Chemistry – Complete Study Guide PDF
Full theory + how-to steps + worked examples for all 9 topics
Uses reportlab canvas for full layout control
"""
from reportlab.lib.pagesizes import A4
from reportlab.lib.units import cm, mm
from reportlab.lib import colors
from reportlab.pdfgen import canvas as pdfcanvas
from reportlab.platypus import Paragraph
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.enums import TA_LEFT, TA_CENTER
W, H = A4
OUT = "/home/daytona/workspace/chem-cheatsheet/HonorsChem_StudyGuide_Complete.pdf"
# ── Colours ───────────────────────────────────────────────────
BG_TITLE = colors.HexColor("#1a237e")
BG_TOPIC = colors.HexColor("#1565c0")
BG_THEORY = colors.HexColor("#e3f2fd")
BG_HOWTO = colors.HexColor("#e8f5e9")
BG_EXAMPLE = colors.HexColor("#fff3e0")
BG_WARN = colors.HexColor("#fce4ec")
BG_PAGE = colors.HexColor("#fafafa")
CLR_THEORY = colors.HexColor("#0d47a1")
CLR_HOWTO = colors.HexColor("#1b5e20")
CLR_EX = colors.HexColor("#e65100")
CLR_WARN = colors.HexColor("#b71c1c")
BLACK = colors.black
GREY = colors.HexColor("#37474f")
WHITE = colors.white
DIVIDER = colors.HexColor("#90a4ae")
MARGIN = 14*mm
COL_GAP = 5*mm
COL_W = (W - 2*MARGIN - COL_GAP) / 2
class PageMaker:
def __init__(self, filename):
self.c = pdfcanvas.Canvas(filename, pagesize=A4)
self.page_num = 0
self._new_page()
def _new_page(self):
self.page_num += 1
self.c.setPageSize(A4)
# light background
self.c.setFillColor(BG_PAGE)
self.c.rect(0, 0, W, H, fill=1, stroke=0)
# footer
self.c.setFillColor(BG_TITLE)
self.c.rect(0, 0, W, 11, fill=1, stroke=0)
self.c.setFillColor(WHITE)
self.c.setFont("Helvetica", 5.5)
self.c.drawCentredString(W/2, 3.5,
f"Honors Chemistry Final Exam — Complete Study Guide · Page {self.page_num} · Pendleton High School")
def new_page(self):
self.c.showPage()
self._new_page()
def save(self):
self.c.save()
print(f"✅ Saved: {OUT}")
# ── Drawing helpers ────────────────────────────────────────────────
def title_bar(pm, y, text, sub=""):
c = pm.c
bar_h = 22 if sub else 16
c.setFillColor(BG_TITLE)
c.rect(0, y - bar_h, W, bar_h, fill=1, stroke=0)
c.setFillColor(WHITE)
c.setFont("Helvetica-Bold", 12 if sub else 10)
c.drawCentredString(W/2, y - 13, text)
if sub:
c.setFont("Helvetica", 8)
c.drawCentredString(W/2, y - 20, sub)
return y - bar_h - 3*mm
def topic_header(pm, x, y, w, num, title, qs, color=BG_TOPIC):
c = pm.c
c.setFillColor(color)
c.roundRect(x, y - 14, w, 14, 3, fill=1, stroke=0)
c.setFillColor(WHITE)
c.setFont("Helvetica-Bold", 8.5)
c.drawString(x + 4, y - 10.5, f"TOPIC {num}: {title}")
c.setFont("Helvetica", 7)
c.drawRightString(x + w - 4, y - 10.5, f"Exam: {qs}")
return y - 15
def section_label(pm, x, y, w, label, bg, fg):
c = pm.c
c.setFillColor(bg)
c.rect(x, y - 10, w, 10, fill=1, stroke=0)
c.setFillColor(fg)
c.setFont("Helvetica-Bold", 6.5)
c.drawString(x + 3, y - 7.5, label)
return y - 10
def text_block(pm, x, y, w, lines, font="Helvetica", size=6.8, color=BLACK,
bg=None, border=None, line_h=9, padding_x=4, padding_y=3):
"""Draw a block of text lines with optional background box. Returns new y."""
c = pm.c
total_h = len(lines) * line_h + 2 * padding_y
if bg:
c.setFillColor(bg)
lw = 0.4 if border else 0
if border:
c.setStrokeColor(border)
c.setLineWidth(lw)
c.rect(x, y - total_h, w, total_h, fill=1, stroke=1 if border else 0)
c.setFillColor(color)
c.setFont(font, size)
for i, line in enumerate(lines):
# bold prefix support: lines starting with "• " get normal treatment
c.drawString(x + padding_x, y - padding_y - i * line_h - size + 1, line)
return y - total_h - 1.5*mm
def bullet_block(pm, x, y, w, items, bg=None, border=None,
size=6.8, line_h=9, padding_x=4, padding_y=3):
"""Bullet list with optional box."""
c = pm.c
total_h = len(items) * line_h + 2 * padding_y
if bg:
c.setFillColor(bg)
if border:
c.setStrokeColor(border)
c.setLineWidth(0.4)
c.rect(x, y - total_h, w, total_h, fill=1, stroke=1 if border else 0)
for i, item in enumerate(items):
c.setFillColor(CLR_HOWTO if bg == BG_HOWTO else GREY)
c.setFont("Helvetica-Bold", size)
c.drawString(x + padding_x, y - padding_y - i * line_h - size + 1, "→")
c.setFillColor(BLACK)
c.setFont("Helvetica", size)
c.drawString(x + padding_x + 10, y - padding_y - i * line_h - size + 1, item)
return y - total_h - 1.5*mm
def steps_block(pm, x, y, w, steps, bg=BG_HOWTO, border_color=CLR_HOWTO,
size=6.8, line_h=9):
"""Numbered steps block."""
c = pm.c
padding_x, padding_y = 4, 3
total_h = len(steps) * line_h + 2 * padding_y
c.setFillColor(bg)
c.setStrokeColor(border_color)
c.setLineWidth(0.5)
c.rect(x, y - total_h, w, total_h, fill=1, stroke=1)
for i, step in enumerate(steps):
c.setFillColor(border_color)
c.setFont("Helvetica-Bold", size)
c.drawString(x + padding_x, y - padding_y - i * line_h - size + 1, f"Step {i+1}.")
c.setFillColor(BLACK)
c.setFont("Helvetica", size)
c.drawString(x + padding_x + 32, y - padding_y - i * line_h - size + 1, step)
return y - total_h - 1.5*mm
def example_block(pm, x, y, w, lines, title="WORKED EXAMPLE"):
"""Orange example box."""
c = pm.c
size = 6.8
line_h = 8.5
padding_x, padding_y = 4, 3
total_h = (len(lines) + 1) * line_h + 2 * padding_y
c.setFillColor(BG_EXAMPLE)
c.setStrokeColor(CLR_EX)
c.setLineWidth(0.6)
c.rect(x, y - total_h, w, total_h, fill=1, stroke=1)
c.setFillColor(CLR_EX)
c.setFont("Helvetica-Bold", 6.5)
c.drawString(x + padding_x, y - padding_y - size + 1, f"✎ {title}")
c.setFillColor(GREY)
c.setFont("Helvetica", size)
for i, line in enumerate(lines):
c.drawString(x + padding_x + 4, y - padding_y - (i+1) * line_h - size + 1, line)
return y - total_h - 1.5*mm
def warn_box(pm, x, y, w, lines):
"""Red warning/tip box."""
c = pm.c
size = 6.8
line_h = 8.5
padding_x, padding_y = 4, 3
total_h = len(lines) * line_h + 2 * padding_y
c.setFillColor(BG_WARN)
c.setStrokeColor(CLR_WARN)
c.setLineWidth(0.6)
c.rect(x, y - total_h, w, total_h, fill=1, stroke=1)
c.setFillColor(CLR_WARN)
c.setFont("Helvetica-Bold", 6.5)
c.drawString(x + padding_x, y - padding_y - size + 1, "⚠ REMEMBER:")
c.setFont("Helvetica", size)
for i, line in enumerate(lines):
c.drawString(x + padding_x + 52, y - padding_y - i * line_h - size + 1, line)
return y - total_h - 1.5*mm
def table_block(pm, x, y, w, headers, rows, col_ratios=None, size=6.5, line_h=8.5):
"""Simple table."""
c = pm.c
ncols = len(headers)
if col_ratios is None:
col_ratios = [1/ncols] * ncols
col_widths = [w * r for r in col_ratios]
row_h = line_h + 2
total_h = row_h * (len(rows) + 1)
# header row
cx = x
c.setFillColor(BG_TOPIC)
c.rect(x, y - row_h, w, row_h, fill=1, stroke=0)
c.setFillColor(WHITE)
c.setFont("Helvetica-Bold", size)
for h, cw in zip(headers, col_widths):
c.drawString(cx + 3, y - row_h + 3, h)
cx += cw
# data rows
for ri, row in enumerate(rows):
row_y = y - row_h * (ri + 2)
bg = colors.HexColor("#eceff1") if ri % 2 == 0 else WHITE
c.setFillColor(bg)
c.rect(x, row_y, w, row_h, fill=1, stroke=0)
c.setStrokeColor(DIVIDER)
c.setLineWidth(0.2)
c.line(x, row_y, x + w, row_y)
cx = x
c.setFillColor(BLACK)
c.setFont("Helvetica", size)
for cell, cw in zip(row, col_widths):
c.drawString(cx + 3, row_y + 3, str(cell))
cx += cw
# outer border
c.setStrokeColor(BG_TOPIC)
c.setLineWidth(0.5)
c.rect(x, y - total_h, w, total_h, fill=0, stroke=1)
return y - total_h - 1.5*mm
def divider(pm, x, y, w):
pm.c.setStrokeColor(DIVIDER)
pm.c.setLineWidth(0.4)
pm.c.line(x, y, x + w, y)
return y - 2*mm
def gap(y, n=1):
return y - n * 2*mm
# ═══════════════════════════════════════════════════════════════════
# BUILD THE DOCUMENT
# ═══════════════════════════════════════════════════════════════════
pm = PageMaker(OUT)
c = pm.c
# ── COVER / TITLE PAGE ────────────────────────────────────────────
y = H - 15*mm
c.setFillColor(BG_TITLE)
c.rect(0, y - 60, W, 60, fill=1, stroke=0)
c.setFillColor(WHITE)
c.setFont("Helvetica-Bold", 20)
c.drawCentredString(W/2, y - 25, "HONORS CHEMISTRY")
c.setFont("Helvetica-Bold", 14)
c.drawCentredString(W/2, y - 40, "FINAL EXAM — COMPLETE STUDY GUIDE")
c.setFont("Helvetica", 9)
c.drawCentredString(W/2, y - 52, "Theory · How to Solve · Worked Examples · All 9 Exam Topics · Pendleton High School")
y -= 68
# intro box
c.setFillColor(colors.HexColor("#e8eaf6"))
c.setStrokeColor(BG_TITLE)
c.setLineWidth(0.8)
c.rect(MARGIN, y - 36, W - 2*MARGIN, 36, fill=1, stroke=1)
c.setFillColor(BG_TITLE)
c.setFont("Helvetica-Bold", 8)
c.drawString(MARGIN + 5, y - 11, "HOW TO USE THIS GUIDE:")
c.setFillColor(BLACK)
c.setFont("Helvetica", 7.5)
intro = [
"1. Read the THEORY section — understand WHAT a concept is and WHY it matters.",
"2. Read HOW TO SOLVE — these are the exact steps to follow on the test.",
"3. Follow the WORKED EXAMPLE — trace through it yourself to check you understand.",
"The cheat sheet you bring to the test has the theory + steps but NOT the examples.",
]
for i, ln in enumerate(intro):
c.drawString(MARGIN + 5, y - 22 - i * 9, ln)
y -= 44
# ── Two-column layout from here ────────────────────────────────────
# We'll place topics alternating left/right, flowing down the page.
# Each topic starts fresh in left col if remaining space < threshold.
LX = MARGIN # left column x
RX = MARGIN + COL_W + COL_GAP # right column x
def check_space(y_val, needed, pm_obj):
"""If not enough space, start new page and return top y."""
if y_val < 18*mm + needed:
pm_obj.new_page()
return H - 15*mm
return y_val
# ════════════════════════════════════════════════════════════════
# TOPIC 1: ATOMIC STRUCTURE & THE PERIODIC TABLE
# ════════════════════════════════════════════════════════════════
y = check_space(y, 40, pm)
y = topic_header(pm, LX, y, W - 2*MARGIN, 1,
"ATOMIC STRUCTURE & THE PERIODIC TABLE", "Questions 44–50")
# --- split into left and right below topic header ---
ly = y - 2*mm
ry = y - 2*mm
# LEFT: Vocabulary
ly = section_label(pm, LX, ly, COL_W, "▶ KEY VOCABULARY", BG_THEORY, CLR_THEORY)
vocab_items = [
("Ground state", "The lowest energy state of an atom — electrons are in the closest possible shells."),
("Excited state", "When an electron ABSORBS energy and jumps to a HIGHER shell."),
("Isotopes", "Same element (same protons), different number of NEUTRONS → different mass. Example: C-12 vs C-14."),
("Allotropes", "Same element, different physical structure. Diamond vs graphite — both are pure carbon (C)."),
("Alloy", "A MIXTURE with metallic properties. Steel = Fe + C. Bronze = Cu + Sn."),
("Octet rule", "Atoms are most stable when they have 8 electrons in their outer shell."),
]
for term, defn in vocab_items:
c.setFillColor(BG_THEORY)
c.rect(LX, ly - 20, COL_W, 20, fill=1, stroke=0)
c.setFillColor(CLR_THEORY)
c.setFont("Helvetica-Bold", 7)
c.drawString(LX + 4, ly - 9, term)
c.setFillColor(BLACK)
c.setFont("Helvetica", 6.5)
# wrap definition
words = defn.split()
line1 = ""
line2 = ""
for w in words:
if len(line1) + len(w) + 1 < 52:
line1 += (" " if line1 else "") + w
else:
line2 += (" " if line2 else "") + w
c.drawString(LX + 4, ly - 16, line1)
if line2:
# slightly smaller for overflow
c.setFont("Helvetica", 6)
c.drawString(LX + 4, ly - 22.5, line2)
ly -= 26
else:
ly -= 22
ly -= 1
ly -= 3
# LEFT: Electron configuration
ly = section_label(pm, LX, ly, COL_W, "▶ ELECTRON CONFIGURATION — THEORY", BG_THEORY, CLR_THEORY)
ly = text_block(pm, LX, ly, COL_W, [
"Electrons fill orbitals from LOWEST to HIGHEST energy.",
"Each orbital type holds a set number of electrons:",
" s = 2 electrons | p = 6 | d = 10 | f = 14",
"Fill order (Aufbau principle):",
" 1s → 2s → 2p → 3s → 3p → 4s → 3d → 4p → 5s → 4d → 5p",
" ⚠ 4s fills BEFORE 3d — this is a common exam trap!",
"Shortcut: [noble gas] = use the previous noble gas config",
" then continue filling from where it stopped.",
], bg=BG_THEORY, border=CLR_THEORY, line_h=8.5)
# RIGHT: Light & photon energy
ry = section_label(pm, RX, ry, COL_W, "▶ LIGHT & ELECTROMAGNETIC SPECTRUM — THEORY", BG_THEORY, CLR_THEORY)
ry = text_block(pm, RX, ry, COL_W, [
"Light travels as both a wave AND packets of energy (photons).",
"Three related quantities — wavelength (λ), frequency (ν), energy (E):",
"",
" c = λ × ν → ν = c / λ",
" E = h × ν → E = hc / λ",
"",
" c = 3.00 × 10⁸ m/s (speed of light)",
" h = 6.626 × 10⁻³⁴ J·s (Planck's constant)",
" λ in METERS | ν in Hz (s⁻¹) | E in Joules",
"",
"Key relationship: HIGHER frequency = SHORTER wavelength = MORE energy",
], bg=BG_THEORY, border=CLR_THEORY, line_h=8.5)
ry = section_label(pm, RX, ry, COL_W, "▶ HOW TO SOLVE: PHOTON ENERGY PROBLEMS", BG_HOWTO, CLR_HOWTO)
ry = steps_block(pm, RX, ry, COL_W, [
"Identify what you're given: λ (wavelength) or ν (frequency)?",
"If given λ, convert to meters first (nm × 10⁻⁹ = m)",
"Find frequency: ν = c / λ",
"Find energy: E = h × ν",
"Check units: answer should be in Joules (J)",
])
ry = example_block(pm, RX, ry, COL_W, [
"λ = 500 nm. Find frequency and energy.",
"Convert: 500 nm = 500 × 10⁻⁹ = 5.00 × 10⁻⁷ m",
"ν = c/λ = (3.00×10⁸) / (5.00×10⁻⁷) = 6.00 × 10¹⁴ Hz",
"E = hν = (6.626×10⁻³⁴)(6.00×10¹⁴) = 3.98 × 10⁻¹⁹ J",
])
# HOW TO SOLVE config — goes in left col
ly = section_label(pm, LX, ly, COL_W, "▶ HOW TO SOLVE: ELECTRON CONFIGURATION", BG_HOWTO, CLR_HOWTO)
ly = steps_block(pm, LX, ly, COL_W, [
"Look up (or count) the atomic number Z",
"Find the PREVIOUS noble gas in the periodic table",
"Write [that noble gas] as the shortcut",
"Continue filling: 4s → 3d → 4p (remember 4s before 3d!)",
"Count electrons added = Z minus noble gas Z",
])
ly = example_block(pm, LX, ly, COL_W, [
"Germanium (Ge): Z = 32, Period 4, Group 14",
"Previous noble gas: Ar (Z=18) → write [Ar]",
"Need 32 − 18 = 14 more electrons",
"Fill: 4s²(2) → 3d¹⁰(12) → 4p²(14) ✓",
"Answer: [Ar] 4s² 3d¹⁰ 4p²",
])
ly = warn_box(pm, LX, ly, COL_W, [
"4s fills BEFORE 3d — always!",
"Transition metals start at group 3 (filling 3d subshell)",
])
# sync columns
y = min(ly, ry) - 3*mm
y = divider(pm, MARGIN, y, W - 2*MARGIN)
# ════════════════════════════════════════════════════════════════
# TOPIC 2: CHEMICAL BONDING
# ════════════════════════════════════════════════════════════════
y = check_space(y, 60, pm)
y = topic_header(pm, LX, y, W - 2*MARGIN, 2,
"CHEMICAL BONDING", "Questions 32–40",
color=colors.HexColor("#4a148c"))
ly = y - 2*mm
ry = y - 2*mm
ly = section_label(pm, LX, ly, COL_W, "▶ TYPES OF BONDS — THEORY", BG_THEORY, CLR_THEORY)
ly = table_block(pm, LX, ly, COL_W,
["Type", "How it works", "Who", "Property"],
[
["Ionic", "Metal GIVES e⁻ to nonmetal", "Metal + Nonmetal", "High MP, forms crystals"],
["Covalent", "Both atoms SHARE electrons", "Nonmetal+Nonmetal", "Lower MP, no ions"],
["Coordinate", "ONE atom donates BOTH electrons", "Special covalent", "e.g. NH₄⁺, H₃O⁺"],
],
col_ratios=[0.18, 0.36, 0.28, 0.18]
)
ly = section_label(pm, LX, ly, COL_W, "▶ SIGMA & PI BONDS — THEORY", BG_THEORY, CLR_THEORY)
ly = text_block(pm, LX, ly, COL_W, [
"Every bond contains one sigma (σ) bond (electron density BETWEEN atoms).",
"Extra bonds in double/triple bonds are pi (π) bonds (above/below axis).",
" Single bond = 1σ, 0π",
" Double bond = 1σ, 1π",
" Triple bond = 1σ, 2π ← strongest and shortest",
], bg=BG_THEORY, border=CLR_THEORY, line_h=8.5)
ly = section_label(pm, LX, ly, COL_W, "▶ BOND LENGTH & STRENGTH — THEORY", BG_THEORY, CLR_THEORY)
ly = text_block(pm, LX, ly, COL_W, [
"SHORTER bond = STRONGER bond = HIGHER bond dissociation energy",
" Triple > Double > Single (strength and energy to break)",
" Single > Double > Triple (length)",
], bg=BG_THEORY, border=CLR_THEORY, line_h=8.5)
# Right col: polarity + diatomic
ry = section_label(pm, RX, ry, COL_W, "▶ POLARITY & MOLECULAR GEOMETRY — THEORY", BG_THEORY, CLR_THEORY)
ry = text_block(pm, RX, ry, COL_W, [
"A bond is polar when two atoms have DIFFERENT electronegativities",
"(one atom pulls electrons more strongly — creates δ+ and δ− ends).",
"",
"NONPOLAR molecule: bond dipoles CANCEL each other out.",
" → Must be perfectly SYMMETRIC geometry.",
" → Example: SiH₄ (tetrahedral, 4 equal bonds) = NONPOLAR",
"",
"POLAR molecule: bond dipoles do NOT cancel.",
" → Usually happens when central atom has LONE PAIRS.",
" → Lone pairs push the geometry, making it asymmetric.",
" → Examples: AsH₃, PH₃, H₂S — all polar",
], bg=BG_THEORY, border=CLR_THEORY, line_h=8.5)
ry = section_label(pm, RX, ry, COL_W, "▶ DIATOMIC ELEMENTS — THEORY", BG_THEORY, CLR_THEORY)
ry = text_block(pm, RX, ry, COL_W, [
"7 elements exist as DIATOMIC pairs in nature — they never go solo.",
"Memory trick: HOFBrINCl",
" H₂ O₂ F₂ Br₂ I₂ N₂ Cl₂",
"Always write these with subscript 2 in chemical equations!",
"",
"Noble gases (He, Ne, Ar, Kr, Xe) are MONOATOMIC — they never bond.",
"Do NOT write He₂ or Ar₂.",
], bg=BG_THEORY, border=CLR_THEORY, line_h=8.5)
ry = section_label(pm, RX, ry, COL_W, "▶ HOW TO SOLVE: COUNT σ AND π BONDS", BG_HOWTO, CLR_HOWTO)
ry = steps_block(pm, RX, ry, COL_W, [
"Draw or identify all bonds in the molecule",
"Every single bond → count 1σ",
"Every double bond → count 1σ + 1π",
"Every triple bond → count 1σ + 2π",
"Add all σ together, then all π together",
])
ry = example_block(pm, RX, ry, COL_W, [
"Ethene C₂H₄: Structure: H₂C=CH₂",
" C=C → 1σ + 1π",
" 4 C–H single bonds → 4σ",
" TOTAL: 5σ bonds, 1π bond",
])
y = min(ly, ry) - 3*mm
y = divider(pm, MARGIN, y, W - 2*MARGIN)
# ════════════════════════════════════════════════════════════════
# TOPIC 3: CHEMICAL NOMENCLATURE
# ════════════════════════════════════════════════════════════════
y = check_space(y, 60, pm)
y = topic_header(pm, LX, y, W - 2*MARGIN, 3,
"CHEMICAL NOMENCLATURE & FORMULAS", "Questions 27, 35, 36, 41–43",
color=colors.HexColor("#e65100"))
ly = y - 2*mm
ry = y - 2*mm
ly = section_label(pm, LX, ly, COL_W, "▶ IONIC COMPOUNDS — THEORY", BG_THEORY, CLR_THEORY)
ly = text_block(pm, LX, ly, COL_W, [
"Ionic compounds: metal + nonmetal, charges must BALANCE to zero.",
"",
"Naming rules:",
" 1. Write METAL name first (unchanged)",
" 2. If VARIABLE-CHARGE metal (Fe, Cu, Pb, Sn, Hg, Cr, Mn):",
" → Figure out the charge from the formula",
" → Add Roman numeral in parentheses: Fe(III)",
" 3. Write ANION name:",
" Simple nonmetal: add -ide (Cl⁻ = chloride)",
" Polyatomic ion: use its own name (SO₄²⁻ = sulfate)",
"",
" -ate = MORE oxygens | -ite = FEWER oxygens",
" hypo- prefix = fewest oxygens (ClO⁻ = hypochlorite)",
" per- prefix = most oxygens (ClO₄⁻ = perchlorate)",
], bg=BG_THEORY, border=CLR_THEORY, line_h=8.5)
ly = section_label(pm, LX, ly, COL_W, "▶ POLYATOMIC IONS — MUST MEMORIZE", BG_THEORY, CLR_THEORY)
ly = table_block(pm, LX, ly, COL_W,
["Ion", "Name", "Ion", "Name"],
[
["SO₄²⁻", "sulfate", "ClO⁻", "hypochlorite"],
["SO₃²⁻", "sulfite", "ClO₂⁻", "chlorite"],
["NO₃⁻", "nitrate", "ClO₃⁻", "chlorate"],
["NO₂⁻", "nitrite", "ClO₄⁻", "perchlorate"],
["PO₄³⁻", "phosphate", "OH⁻", "hydroxide"],
["NH₄⁺", "ammonium", "CO₃²⁻", "carbonate"],
],
col_ratios=[0.18, 0.32, 0.18, 0.32]
)
ry = section_label(pm, RX, ry, COL_W, "▶ HOW TO SOLVE: NAME AN IONIC COMPOUND", BG_HOWTO, CLR_HOWTO)
ry = steps_block(pm, RX, ry, COL_W, [
"Identify the cation (metal) and anion",
"Is the metal VARIABLE-CHARGE? (Fe, Cu, Pb, Sn, Hg, Cr, Mn)",
" If YES: total negative charge ÷ number of metal atoms = metal charge",
" Write the charge as Roman numeral in ()",
"If NO variable charge: just write the metal name",
"Write the anion name (-ide, -ate, -ite, or polyatomic name)",
])
ry = example_block(pm, RX, ry, COL_W, [
"Fe₂(SO₄)₃: 3 sulfate ions × 2− = 6− total negative",
" 2 Fe atoms must balance: 6÷2 = 3+ per Fe",
" → Iron(III) sulfate",
"Ca₃(PO₄)₂: Ca is Group 2, always 2+ (NOT variable)",
" → Calcium phosphate (no Roman numeral needed)",
])
ry = section_label(pm, RX, ry, COL_W, "▶ HOW TO SOLVE: WRITE AN IONIC FORMULA", BG_HOWTO, CLR_HOWTO)
ry = steps_block(pm, RX, ry, COL_W, [
"Write cation (positive) first, anion (negative) second",
"CROSSOVER METHOD: swap the charge numbers as subscripts",
"If subscript is 1, leave it out (don't write Ca₁Cl₁, just CaCl₂)",
"Reduce if possible: Ca²⁺ + O²⁻ → CaO (not Ca₂O₂)",
"Polyatomic ion with subscript > 1? Wrap in parentheses",
])
ry = example_block(pm, RX, ry, COL_W, [
"Ca²⁺ + PO₄³⁻: cross charges → Ca₃(PO₄)₂",
" Check: 3×(2+) = 6+ and 2×(3−) = 6− ✓",
"Al³⁺ + O²⁻: cross → Al₂O₃",
" Check: 2×(3+) = 6+ and 3×(2−) = 6− ✓",
])
ry = section_label(pm, RX, ry, COL_W, "▶ MOLECULAR COMPOUNDS & ACIDS — THEORY", BG_THEORY, CLR_THEORY)
ry = text_block(pm, RX, ry, COL_W, [
"Molecular compounds: use GREEK PREFIXES for atom count.",
" 1=mono 2=di 3=tri 4=tetra 5=penta 6=hexa 7=hepta 8=octa",
" N₂H₄ = dinitrogen tetrahydride",
"",
"Acids (containing H, dissolved in water):",
" -ate ion → -ic acid: H₂SO₄ = sulfuric acid",
" -ite ion → -ous acid: H₂SO₃ = sulfurous acid",
"",
"Hydrates: Compound • xH₂O",
" Ba(OH)₂ • 8H₂O = barium hydroxide octahydrate",
], bg=BG_THEORY, border=CLR_THEORY, line_h=8.5)
y = min(ly, ry) - 3*mm
y = divider(pm, MARGIN, y, W - 2*MARGIN)
# ════════════════════════════════════════════════════════════════
# TOPIC 4: CHEMICAL REACTIONS
# ════════════════════════════════════════════════════════════════
pm.new_page()
y = H - 15*mm
y = topic_header(pm, LX, y, W - 2*MARGIN, 4,
"CHEMICAL REACTIONS & EQUATIONS", "Questions 28–31",
color=colors.HexColor("#b71c1c"))
ly = y - 2*mm
ry = y - 2*mm
ly = section_label(pm, LX, ly, COL_W, "▶ BALANCING EQUATIONS — THEORY", BG_THEORY, CLR_THEORY)
ly = text_block(pm, LX, ly, COL_W, [
"Law of Conservation of Mass: atoms cannot be created or destroyed.",
"A balanced equation has the SAME number of each atom on both sides.",
"",
"RULES:",
" • Only change COEFFICIENTS (numbers in front of formulas)",
" • NEVER change subscripts inside a formula",
" • Coefficients must be the smallest whole-number ratio",
" • Fractions are allowed during balancing but clear them at the end",
], bg=BG_THEORY, border=CLR_THEORY, line_h=8.5)
ly = section_label(pm, LX, ly, COL_W, "▶ HOW TO SOLVE: BALANCE AN EQUATION", BG_HOWTO, CLR_HOWTO)
ly = steps_block(pm, LX, ly, COL_W, [
"Count atoms of EACH element on both sides",
"Start with elements that appear in FEWEST compounds",
"Add coefficient to balance one element at a time",
"Leave H and O until last (they appear in almost everything)",
"Check ALL elements at the end",
"Clear any fractions by multiplying everything through",
])
ly = example_block(pm, LX, ly, COL_W, [
"SiO₂ + HF → SiF₄ + H₂O",
"Si: 1=1 ✓ | F: need 4 on right → 4HF on left",
"H: 4 on left → 2H₂O on right | O: 2=2 ✓",
"Balanced: SiO₂ + 4HF → SiF₄ + 2H₂O",
"(coefficient for HF = 4)",
])
ly = section_label(pm, LX, ly, COL_W, "▶ REACTION TYPES — THEORY", BG_THEORY, CLR_THEORY)
ly = table_block(pm, LX, ly, COL_W,
["Type", "Pattern", "Example"],
[
["Synthesis", "A + B → AB", "2H₂ + O₂ → 2H₂O"],
["Decomposition", "AB → A + B", "CaCO₃ → CaO + CO₂"],
["Single Replace","A + BC → AC + B", "Zn + HCl → ZnCl₂ + H₂"],
["Double Replace","AB+CD → AD+CB", "AgNO₃+NaCl → AgCl+NaNO₃"],
["Combustion", "HC + O₂ → CO₂ + H₂O", "CH₄+2O₂→CO₂+2H₂O"],
["Neutralization","Acid+Base→Salt+Water", "HCl+NaOH→NaCl+H₂O"],
],
col_ratios=[0.25, 0.35, 0.40]
)
ry = section_label(pm, RX, ry, COL_W, "▶ PRECIPITATES & NET IONIC — THEORY", BG_THEORY, CLR_THEORY)
ry = text_block(pm, RX, ry, COL_W, [
"Precipitate: a solid (s) product that forms when 2 aqueous",
"solutions are mixed. Marked with (s) in the equation.",
"Example: AgNO₃(aq) + NaCl(aq) → AgCl(s) + NaNO₃(aq)",
"",
"Full ionic equation: split all (aq) compounds into their ions.",
" AgNO₃ splits: Ag⁺ + NO₃⁻",
" NaCl splits: Na⁺ + Cl⁻",
"",
"Spectator ions: ions that appear UNCHANGED on both sides.",
" → They don't participate in the reaction. Cross them out.",
"",
"Net ionic equation: what's left after removing spectators.",
" → Shows only the ACTUAL reaction that occurs.",
], bg=BG_THEORY, border=CLR_THEORY, line_h=8.5)
ry = section_label(pm, RX, ry, COL_W, "▶ HOW TO SOLVE: NET IONIC EQUATION", BG_HOWTO, CLR_HOWTO)
ry = steps_block(pm, RX, ry, COL_W, [
"Write and balance the full molecular equation",
"Separate all (aq) ionic compounds into their individual ions",
"Leave (s), (l), (g) species as molecules — don't split them",
"Identify spectator ions: same ion on both sides unchanged",
"Cross out spectators from both sides",
"What remains = the net ionic equation",
])
ry = example_block(pm, RX, ry, COL_W, [
"HCl(aq) + KOH(aq) → KCl(aq) + H₂O(l)",
"Full ionic: H⁺ + Cl⁻ + K⁺ + OH⁻ → K⁺ + Cl⁻ + H₂O",
"Spectators: K⁺ and Cl⁻ (same on both sides — cancel)",
"Net ionic: H⁺(aq) + OH⁻(aq) → H₂O(l)",
])
ry = section_label(pm, RX, ry, COL_W, "▶ NEUTRALIZATION — THEORY & HOW TO", BG_THEORY, CLR_THEORY)
ry = text_block(pm, RX, ry, COL_W, [
"Neutralization: Acid + Base → Salt + Water",
"The acid provides H⁺, the base provides OH⁻.",
"They combine to form water: H⁺ + OH⁻ → H₂O",
"The remaining ions form the salt.",
"",
"Example: Ba(OH)₂ + 2HCl → BaCl₂ + 2H₂O",
" Ba(OH)₂ provides 2 OH⁻ | 2 HCl provides 2 H⁺",
" Products: BaCl₂ (salt) + 2H₂O (water)",
], bg=BG_THEORY, border=CLR_THEORY, line_h=8.5)
y = min(ly, ry) - 3*mm
y = divider(pm, MARGIN, y, W - 2*MARGIN)
# ════════════════════════════════════════════════════════════════
# TOPIC 5: THE MOLE & MOLAR MASS
# ════════════════════════════════════════════════════════════════
y = check_space(y, 60, pm)
y = topic_header(pm, LX, y, W - 2*MARGIN, 5,
"THE MOLE & MOLAR MASS", "Questions 19–26",
color=colors.HexColor("#1b5e20"))
ly = y - 2*mm
ry = y - 2*mm
ly = section_label(pm, LX, ly, COL_W, "▶ WHAT IS A MOLE? — THEORY", BG_THEORY, CLR_THEORY)
ly = text_block(pm, LX, ly, COL_W, [
"A mole is just a COUNTING NUMBER, like 'dozen' = 12.",
" 1 mole = 6.02 × 10²³ particles (Avogadro's number)",
"",
"Why so big? Because atoms are incredibly tiny.",
"One mole of carbon atoms = 12.01 grams of carbon.",
"",
"MOLAR MASS = the mass (in grams) of exactly 1 mole of a substance.",
" • For an element: molar mass = atomic mass from periodic table (g/mol)",
" • For a compound: ADD UP the molar masses of all atoms in formula",
" • The molar mass IS the conversion factor between grams and moles.",
], bg=BG_THEORY, border=CLR_THEORY, line_h=8.5)
ly = section_label(pm, LX, ly, COL_W, "▶ HOW TO SOLVE: MOLE CALCULATIONS", BG_HOWTO, CLR_HOWTO)
ly = steps_block(pm, LX, ly, COL_W, [
"FIRST: Find the molar mass of the substance",
" (add all atomic masses × their subscripts in the formula)",
"grams → moles: moles = grams ÷ molar mass",
"moles → grams: grams = moles × molar mass",
"moles → particles: particles = moles × 6.02 × 10²³",
"particles → moles: moles = particles ÷ 6.02 × 10²³",
])
ly = example_block(pm, LX, ly, COL_W, [
"How many moles in 100 g of Cl₂?",
" MM of Cl₂ = 2 × 35.5 = 71.0 g/mol",
" moles = 100 g ÷ 71.0 g/mol = 1.41 mol",
"Mass of 0.82 mol Fe₂O₃?",
" MM = 2(55.85) + 3(16.00) = 159.7 g/mol",
" grams = 0.82 × 159.7 = 130.9 g ≈ 131 g",
"MM of H₂CrO₄: 2(1) + 52 + 4(16) = 118 g/mol",
])
ry = section_label(pm, RX, ry, COL_W, "▶ PERCENT COMPOSITION — THEORY", BG_THEORY, CLR_THEORY)
ry = text_block(pm, RX, ry, COL_W, [
"Percent composition tells you what FRACTION of a compound's",
"mass comes from each element.",
"",
" % element = (mass of element in formula / molar mass) × 100",
"",
"The 'mass of element in formula' = atomic mass × subscript",
"All percentages in a compound must ADD UP TO 100%.",
], bg=BG_THEORY, border=CLR_THEORY, line_h=8.5)
ry = section_label(pm, RX, ry, COL_W, "▶ HOW TO SOLVE: PERCENT COMPOSITION", BG_HOWTO, CLR_HOWTO)
ry = steps_block(pm, RX, ry, COL_W, [
"Calculate molar mass of the entire compound",
"For each element: find its total mass in 1 formula unit",
" (atomic mass × subscript)",
"Divide element mass by molar mass",
"Multiply by 100 to get percent",
"Verify: all percentages sum to ~100%",
])
ry = example_block(pm, RX, ry, COL_W, [
"% composition of CO₂ (MM = 12 + 32 = 44 g/mol):",
" % C = (12 / 44) × 100 = 27.3%",
" % O = (32 / 44) × 100 = 72.7%",
" Check: 27.3 + 72.7 = 100.0% ✓",
])
ry = section_label(pm, RX, ry, COL_W, "▶ EMPIRICAL FORMULA — THEORY & HOW TO", BG_THEORY, CLR_THEORY)
ry = text_block(pm, RX, ry, COL_W, [
"Empirical formula = the SIMPLEST whole-number ratio of atoms.",
"You can find it from percent composition data.",
], bg=BG_THEORY, border=CLR_THEORY, line_h=8.5)
ry = steps_block(pm, RX, ry, COL_W, [
"Assume 100 g sample → percent numbers become grams directly",
"Divide each element's grams by its atomic mass → gives moles",
"Divide ALL mole values by the SMALLEST mole value",
"Round to nearest whole number → those are your subscripts",
"If you get .5 → multiply all by 2",
"If you get .33 or .67 → multiply all by 3",
])
ry = example_block(pm, RX, ry, COL_W, [
"85.6% C, 14.4% H → assume 100 g",
" C: 85.6 g ÷ 12 g/mol = 7.13 mol",
" H: 14.4 g ÷ 1 g/mol = 14.4 mol",
" Divide by smallest (7.13): C=1.00, H=2.02≈2",
" Empirical formula: CH₂",
])
y = min(ly, ry) - 3*mm
y = divider(pm, MARGIN, y, W - 2*MARGIN)
# ════════════════════════════════════════════════════════════════
# TOPIC 6: STOICHIOMETRY
# ════════════════════════════════════════════════════════════════
pm.new_page()
y = H - 15*mm
y = topic_header(pm, LX, y, W - 2*MARGIN, 6,
"STOICHIOMETRY", "Questions 11–18",
color=colors.HexColor("#880e4f"))
ly = y - 2*mm
ry = y - 2*mm
ly = section_label(pm, LX, ly, COL_W, "▶ STOICHIOMETRY — THEORY", BG_THEORY, CLR_THEORY)
ly = text_block(pm, LX, ly, COL_W, [
"Stoichiometry uses the balanced equation to predict HOW MUCH",
"of a substance is produced or consumed in a reaction.",
"",
"Basis: Law of Conservation of Mass — matter is not created or",
"destroyed, just rearranged.",
"",
"Key idea: the COEFFICIENTS in a balanced equation give the",
"MOLAR RATIOS — how many moles of each substance react/form.",
"",
"Example: C₃H₈ + 5O₂ → 3CO₂ + 4H₂O",
" 1 mol propane reacts with 5 mol O₂ to produce 3 mol CO₂.",
" O₂ : CO₂ ratio = 5 : 3 (comes from coefficients only!)",
], bg=BG_THEORY, border=CLR_THEORY, line_h=8.5)
ly = section_label(pm, LX, ly, COL_W, "▶ HOW TO SOLVE: STOICHIOMETRY", BG_HOWTO, CLR_HOWTO)
ly = steps_block(pm, LX, ly, COL_W, [
"Write the BALANCED chemical equation (never skip this!)",
"Convert GIVEN quantity to MOLES:",
" grams ÷ molar mass = moles",
"Use MOLAR RATIO from balanced equation:",
" moles given × (coeff wanted / coeff given) = moles wanted",
"Convert moles to DESIRED UNIT:",
" moles × molar mass = grams",
" moles × 6.02×10²³ = number of particles",
])
ly = example_block(pm, LX, ly, COL_W, [
"C₃H₈ + 5O₂ → 3CO₂ + 4H₂O",
"How many g of CO₂ from 44.0 g C₃H₈?",
" MM C₃H₈ = 3(12)+8(1) = 44.0 g/mol",
" 44.0 g ÷ 44.0 = 1.00 mol C₃H₈",
" 1.00 mol C₃H₈ × (3 mol CO₂ / 1 mol C₃H₈) = 3.00 mol CO₂",
" 3.00 mol × 44.0 g/mol = 132 g CO₂",
])
ry = section_label(pm, RX, ry, COL_W, "▶ LIMITING REACTANT — THEORY", BG_THEORY, CLR_THEORY)
ry = text_block(pm, RX, ry, COL_W, [
"In real reactions, you rarely have the EXACT right amounts.",
"One reactant runs out first — it LIMITS how much product forms.",
"",
"Limiting reactant: the one that is COMPLETELY consumed first.",
" → Determines the maximum amount of product (theoretical yield).",
"",
"Excess reactant: what's LEFT OVER after the reaction stops.",
" → Some of this reactant remains unreacted.",
"",
"How to identify: see which reactant produces LESS product.",
" The one that makes less = the limiting reactant.",
], bg=BG_THEORY, border=CLR_THEORY, line_h=8.5)
ry = section_label(pm, RX, ry, COL_W, "▶ HOW TO SOLVE: LIMITING REACTANT", BG_HOWTO, CLR_HOWTO)
ry = steps_block(pm, RX, ry, COL_W, [
"Balance the equation",
"Convert BOTH given quantities to moles",
"For EACH reactant separately: use molar ratio to find",
" how much product it COULD make (if the other was excess)",
"The reactant that produces LESS product = LIMITING reactant",
"Use the LIMITING reactant's moles to calculate final product",
])
ry = example_block(pm, RX, ry, COL_W, [
"4Fe + 3O₂ → 2Fe₂O₃ with 5 mol Fe and 4 mol O₂",
" Fe: 5 mol × (2 mol Fe₂O₃ / 4 mol Fe) = 2.50 mol product",
" O₂: 4 mol × (2 mol Fe₂O₃ / 3 mol O₂) = 2.67 mol product",
" Fe makes LESS → Fe is the LIMITING REACTANT",
" Maximum Fe₂O₃ = 2.50 mol",
])
ry = section_label(pm, RX, ry, COL_W, "▶ PERCENT YIELD — THEORY & HOW TO", BG_THEORY, CLR_THEORY)
ry = text_block(pm, RX, ry, COL_W, [
"In real lab experiments, you almost NEVER get the theoretical maximum.",
"Reasons: side reactions, lost material, incomplete reactions.",
"",
"Theoretical yield = max product predicted by stoichiometry (perfect world)",
"Actual yield = product you actually collected in the lab",
"",
" % yield = (actual yield / theoretical yield) × 100",
"",
"% yield is ALWAYS ≤ 100% (if >100% something went wrong!)",
], bg=BG_THEORY, border=CLR_THEORY, line_h=8.5)
ry = example_block(pm, RX, ry, COL_W, [
"Theoretical = 10.0 g, Actual = 8.2 g",
"% yield = (8.2 / 10.0) × 100 = 82%",
"",
"If % yield = 80%, actual = 15 g, find theoretical:",
" 80 = (15 / theoretical) × 100",
" theoretical = 15 / 0.80 = 18.75 g",
])
y = min(ly, ry) - 3*mm
y = divider(pm, MARGIN, y, W - 2*MARGIN)
# ════════════════════════════════════════════════════════════════
# TOPIC 7: THERMOCHEMISTRY
# ════════════════════════════════════════════════════════════════
y = check_space(y, 50, pm)
y = topic_header(pm, LX, y, W - 2*MARGIN, 7,
"THERMOCHEMISTRY & THERMODYNAMICS", "Questions 7–9",
color=colors.HexColor("#006064"))
ly = y - 2*mm
ry = y - 2*mm
ly = section_label(pm, LX, ly, COL_W, "▶ SPECIFIC HEAT — THEORY", BG_THEORY, CLR_THEORY)
ly = text_block(pm, LX, ly, COL_W, [
"When you heat something, energy (q) flows into it.",
"How much energy depends on:",
" • mass (m) — more mass = more energy needed",
" • specific heat (c) — a property of the material",
" (how much energy to raise 1 gram by 1°C)",
" • temperature change (ΔT = T_final − T_initial)",
"",
" q = m × c × ΔT",
"",
" q > 0 = heat absorbed (endothermic, temperature rises)",
" q < 0 = heat released (exothermic, temperature drops)",
"",
"Common specific heat values:",
" Water: c = 4.18 J/g·°C | Aluminum: c = 0.900 J/g·°C",
], bg=BG_THEORY, border=CLR_THEORY, line_h=8.5)
ly = section_label(pm, LX, ly, COL_W, "▶ HOW TO SOLVE: HEAT PROBLEMS", BG_HOWTO, CLR_HOWTO)
ly = steps_block(pm, LX, ly, COL_W, [
"Identify m (grams), c (given or use common values), T_i, T_f",
"Calculate ΔT = T_final − T_initial (can be negative!)",
"Plug into: q = m × c × ΔT",
"Units: grams × J/g·°C × °C = J ✓",
])
ly = example_block(pm, LX, ly, COL_W, [
"30.0 g Al heated from 20.0°C to 40.0°C",
" c(Al) = 0.900 J/g·°C",
" ΔT = 40.0 − 20.0 = 20.0°C",
" q = 30.0 × 0.900 × 20.0 = 540 J",
])
ry = section_label(pm, RX, ry, COL_W, "▶ CALORIE CONVERSION — THEORY & HOW TO", BG_THEORY, CLR_THEORY)
ry = text_block(pm, RX, ry, COL_W, [
"On food labels: 1 Calorie (capital C) = 1 kilocalorie = 1 kcal",
"In chemistry: 1 calorie (lowercase c) = 4.184 J",
"",
" 1 Cal (food) = 1 kcal = 4184 J",
"",
"To convert Calories to Joules: multiply by 4184",
"To convert Joules to Calories: divide by 4184",
], bg=BG_THEORY, border=CLR_THEORY, line_h=8.5)
ry = example_block(pm, RX, ry, COL_W, [
"170 Calories → Joules:",
" 170 × 4184 = 711,280 J = 7.11 × 10⁵ J",
])
ry = section_label(pm, RX, ry, COL_W, "▶ SPONTANEITY (GIBBS FREE ENERGY) — THEORY", BG_THEORY, CLR_THEORY)
ry = text_block(pm, RX, ry, COL_W, [
"A spontaneous reaction happens ON ITS OWN without constant energy input.",
"",
"Two factors determine spontaneity:",
" ΔH (enthalpy): energy change | ΔS (entropy): disorder change",
" ΔH < 0 = exothermic (releases heat) = FAVORS spontaneity",
" ΔS > 0 = more disorder/randomness = FAVORS spontaneity",
"",
"The Gibbs Free Energy equation (concept only): ΔG = ΔH − TΔS",
" ΔG < 0 = spontaneous | ΔG > 0 = non-spontaneous",
"",
"MOST IMPORTANT RULE FOR THE EXAM:",
" If ΔH < 0 AND ΔS > 0 → ALWAYS spontaneous at ANY temperature",
" (ΔG will always be negative no matter what T is)",
], bg=BG_THEORY, border=CLR_THEORY, line_h=8.5)
ry = table_block(pm, RX, ry, COL_W,
["ΔH", "ΔS", "Spontaneous?"],
[
["< 0 (exo)", "> 0 (more disorder)", "YES, always (any T)"],
["< 0 (exo)", "< 0 (less disorder)", "Only at LOW temperature"],
["> 0 (endo)", "> 0 (more disorder)", "Only at HIGH temperature"],
["> 0 (endo)", "< 0 (less disorder)", "NEVER spontaneous"],
],
col_ratios=[0.22, 0.30, 0.48]
)
y = min(ly, ry) - 3*mm
y = divider(pm, MARGIN, y, W - 2*MARGIN)
# ════════════════════════════════════════════════════════════════
# TOPIC 8: REACTION KINETICS
# ════════════════════════════════════════════════════════════════
pm.new_page()
y = H - 15*mm
y = topic_header(pm, LX, y, W - 2*MARGIN, 8,
"REACTION KINETICS", "Questions 3–6",
color=colors.HexColor("#33691e"))
ly = y - 2*mm
ry = y - 2*mm
ly = section_label(pm, LX, ly, COL_W, "▶ COLLISION THEORY — THEORY", BG_THEORY, CLR_THEORY)
ly = text_block(pm, LX, ly, COL_W, [
"For a chemical reaction to occur, THREE conditions must ALL be met:",
"",
" 1. COLLISION: the particles (atoms, ions, or molecules) must collide.",
" If they never meet, no reaction can happen.",
"",
" 2. ACTIVATION ENERGY: the collision must have ENOUGH energy to",
" break the bonds in the reactants. The minimum energy needed is",
" called the activation energy (Ea).",
"",
" 3. ORIENTATION: particles must collide at the RIGHT angle/position",
" for bonds to form in the product. A head-on crash at the wrong",
" part of the molecule won't work.",
"",
"Reaction rate = how fast reactants turn into products.",
], bg=BG_THEORY, border=CLR_THEORY, line_h=8.5)
ly = section_label(pm, LX, ly, COL_W, "▶ ACTIVATED COMPLEX — THEORY", BG_THEORY, CLR_THEORY)
ly = text_block(pm, LX, ly, COL_W, [
"When reactant particles collide with enough energy, they form a",
"brief, unstable HIGH-ENERGY arrangement called the ACTIVATED COMPLEX",
"(also called the transition state).",
"",
" • It exists ONLY at the PEAK of the energy diagram",
" • It is NEITHER reactant nor product",
" • It immediately either falls apart back to reactants,",
" OR rearranges into products",
" • It cannot be isolated in the lab",
"",
"On an energy diagram: reactants → hill (activated complex) → products",
"The HEIGHT of the hill = activation energy (Ea)",
], bg=BG_THEORY, border=CLR_THEORY, line_h=8.5)
ly = section_label(pm, LX, ly, COL_W, "▶ FACTORS AFFECTING RATE — THEORY", BG_THEORY, CLR_THEORY)
ly = table_block(pm, LX, ly, COL_W,
["Factor", "Effect on Rate", "Why"],
[
["↑ Temperature", "INCREASES", "Faster particles → more collisions with enough energy"],
["Add catalyst", "INCREASES", "Lowers Ea (energy hill) → more collisions succeed"],
["↑ Concentration", "INCREASES", "More particles per volume → more collisions"],
["↑ Surface area", "INCREASES", "More exposed particles → more collisions possible"],
["Remove catalyst", "DECREASES", "Ea goes back up"],
],
col_ratios=[0.25, 0.20, 0.55]
)
ry = section_label(pm, RX, ry, COL_W, "▶ CATALYSTS — THEORY (IMPORTANT!)", BG_THEORY, CLR_THEORY)
ry = text_block(pm, RX, ry, COL_W, [
"A catalyst is a substance that SPEEDS UP a reaction WITHOUT being",
"consumed — it is present at the start and end unchanged.",
"",
"How it works: provides an ALTERNATIVE REACTION PATHWAY with a",
"LOWER activation energy (lower energy hill to climb).",
"",
"What a catalyst DOES change:",
" ✓ Lowers activation energy (Ea)",
" ✓ Increases reaction rate",
"",
"What a catalyst does NOT change:",
" ✗ ΔH (overall energy difference between reactants and products)",
" ✗ The equilibrium position",
" ✗ The identity of products",
], bg=BG_THEORY, border=CLR_THEORY, line_h=8.5)
ry = section_label(pm, RX, ry, COL_W, "▶ HOW TO ANSWER KINETICS QUESTIONS", BG_HOWTO, CLR_HOWTO)
ry = steps_block(pm, RX, ry, COL_W, [
"Rate question: identify what factor changed (temp/conc/catalyst)",
"Temp ↑ → rate ↑ (more collisions AND more energy per collision)",
"Concentration ↑ → rate ↑ (more particles to collide)",
"Catalyst added → rate ↑ + Ea ↓ (but ΔH unchanged!)",
"Catalyst question: always state BOTH effects (rate up, Ea down)",
"Activated complex = at peak of energy diagram, unstable, not product",
])
ry = example_block(pm, RX, ry, COL_W, [
"Q: What happens when a catalyst is added?",
"A: The reaction rate INCREASES because the activation energy",
" (Ea) is LOWERED, so more collisions have enough energy.",
" The overall ΔH (energy of reaction) does NOT change.",
"",
"Q: What is the activated complex?",
"A: The unstable, high-energy intermediate formed when",
" reactant molecules collide — it exists momentarily at",
" the energy maximum, it is neither reactant nor product.",
])
y = min(ly, ry) - 3*mm
y = divider(pm, MARGIN, y, W - 2*MARGIN)
# ════════════════════════════════════════════════════════════════
# TOPIC 9: CHEMICAL EQUILIBRIUM
# ════════════════════════════════════════════════════════════════
y = check_space(y, 50, pm)
y = topic_header(pm, LX, y, W - 2*MARGIN, 9,
"CHEMICAL EQUILIBRIUM", "Questions 1–2",
color=colors.HexColor("#37474f"))
ly = y - 2*mm
ry = y - 2*mm
ly = section_label(pm, LX, ly, COL_W, "▶ EQUILIBRIUM — THEORY", BG_THEORY, CLR_THEORY)
ly = text_block(pm, LX, ly, COL_W, [
"Many reactions are REVERSIBLE — products can react to re-form reactants.",
"",
" A + B ⇌ C + D (double arrow = reversible)",
"",
"Equilibrium is reached when:",
" RATE of FORWARD reaction = RATE of REVERSE reaction",
"",
"CRITICAL: At equilibrium, BOTH reactions are still happening!",
" Equilibrium does NOT mean the reactions stopped.",
" It means they happen at the SAME rate — so concentrations,",
" color, pressure stay CONSTANT (macroscopic properties don't change).",
"",
" The system is DYNAMIC (still changing at the molecular level)",
" but STATIC in appearance.",
], bg=BG_THEORY, border=CLR_THEORY, line_h=8.5)
ly = section_label(pm, LX, ly, COL_W, "▶ EQUILIBRIUM CONSTANT Keq — THEORY", BG_THEORY, CLR_THEORY)
ly = text_block(pm, LX, ly, COL_W, [
"Keq is a number that describes the RATIO of products to reactants",
"at equilibrium for a given reaction at a specific temperature.",
"",
" Keq > 1 → more products than reactants at equilibrium",
" Keq < 1 → more reactants than products at equilibrium",
"",
"MOST IMPORTANT RULES:",
" 1. Keq has ONE valid value at a given TEMPERATURE",
" 2. Changing TEMPERATURE → Keq VALUE changes",
" 3. Changing concentration or pressure → equilibrium SHIFTS",
" (Le Chatelier's Principle), but Keq VALUE stays the SAME",
], bg=BG_THEORY, border=CLR_THEORY, line_h=8.5)
ry = section_label(pm, RX, ry, COL_W, "▶ LE CHATELIER'S PRINCIPLE — THEORY", BG_THEORY, CLR_THEORY)
ry = text_block(pm, RX, ry, COL_W, [
"If a stress is applied to a system at equilibrium, the system",
"will SHIFT to RELIEVE that stress.",
"",
" A + B ⇌ C + D",
"",
"Types of stresses and how the system responds:",
" Add REACTANT (A or B) → shifts RIGHT (→) to make more C and D",
" Add PRODUCT (C or D) → shifts LEFT (←) to remove excess",
" Remove REACTANT → shifts LEFT (←) to replace it",
" Remove PRODUCT → shifts RIGHT (→) to replace it",
" Increase PRESSURE → shifts toward side with FEWER moles of gas",
" Decrease PRESSURE → shifts toward side with MORE moles of gas",
" Increase TEMPERATURE (endothermic rxn) → shifts RIGHT",
" Increase TEMPERATURE (exothermic rxn) → shifts LEFT",
"",
"⚠ Concentration/pressure shifts DO NOT change Keq!",
"⚠ Only temperature changes the actual VALUE of Keq.",
], bg=BG_THEORY, border=CLR_THEORY, line_h=8.5)
ry = section_label(pm, RX, ry, COL_W, "▶ HOW TO ANSWER EQUILIBRIUM QUESTIONS", BG_HOWTO, CLR_HOWTO)
ry = steps_block(pm, RX, ry, COL_W, [
"Identify: is the question about (a) what equilibrium IS,",
" (b) what happens when something is changed (Le Chatelier),",
" or (c) what affects Keq?",
"(a) At equilibrium: fwd rate = rev rate. Both still occurring.",
"(b) Le Chatelier: identify the stress → state which way it shifts",
"(c) Keq changes: ONLY temperature changes Keq value.",
" Adding concentration/pressure = shift, not Keq change.",
])
ry = example_block(pm, RX, ry, COL_W, [
"Q: What does equilibrium mean?",
"A: Forward and reverse reaction rates are EQUAL.",
" Both reactions still occur — concentrations stay constant.",
"",
"Q: Add more reactant — what happens to Keq?",
"A: Keq does NOT change. The equilibrium shifts RIGHT to",
" consume the added reactant, but Keq value stays the same.",
" (Only changing temperature would change Keq.)",
])
y = min(ly, ry) - 3*mm
# Final page — FORMULA QUICK REFERENCE
pm.new_page()
y = H - 15*mm
c = pm.c
# big header
c.setFillColor(BG_TITLE)
c.rect(0, y - 16, W, 16, fill=1, stroke=0)
c.setFillColor(WHITE)
c.setFont("Helvetica-Bold", 11)
c.drawCentredString(W/2, y - 12, "MASTER FORMULA & REFERENCE SHEET — Everything in One Place")
y -= 20
# two column
ly = y
ry = y
# Left: all formulas
ly = section_label(pm, LX, ly, COL_W, "▶ ALL FORMULAS YOU NEED", BG_THEORY, CLR_THEORY)
formulas = [
("Photon frequency", "ν = c / λ", "c=3.00×10⁸ m/s, λ in meters"),
("Photon energy", "E = h × ν = hc/λ", "h=6.626×10⁻³⁴ J·s"),
("Specific heat", "q = m × c × ΔT", "ΔT = T_final − T_initial"),
("Calorie conversion", "1 Cal = 4184 J", "food Calorie = 1 kcal"),
("Moles from grams", "n = mass / MM", "MM = molar mass (g/mol)"),
("Grams from moles", "g = moles × MM", ""),
("Avogadro", "1 mol = 6.02×10²³", "particles per mole"),
("% composition", "% = (elem/MM) × 100", "add all % to check = 100"),
("% yield", "% = actual/theory×100","always ≤ 100%"),
("Molar ratio", "coeff_want/coeff_have","from balanced equation"),
]
row_h = 12
c.setFillColor(BG_THEORY)
total_h = len(formulas) * row_h + 4
c.rect(LX, ly - total_h, COL_W, total_h, fill=1, stroke=0)
c.setStrokeColor(CLR_THEORY)
c.setLineWidth(0.4)
c.rect(LX, ly - total_h, COL_W, total_h, fill=0, stroke=1)
for i, (name, formula, note) in enumerate(formulas):
row_y = ly - 2 - i * row_h
if i % 2 == 0:
c.setFillColor(colors.HexColor("#ddeeff"))
c.rect(LX + 0.5, row_y - row_h + 0.5, COL_W - 1, row_h - 0.5, fill=1, stroke=0)
c.setFillColor(CLR_THEORY)
c.setFont("Helvetica-Bold", 6.3)
c.drawString(LX + 3, row_y - 8, name)
c.setFillColor(colors.HexColor("#b71c1c"))
c.setFont("Courier-Bold", 7)
c.drawString(LX + 68, row_y - 8, formula)
c.setFillColor(GREY)
c.setFont("Helvetica", 5.8)
c.drawString(LX + 155, row_y - 8, note)
ly -= total_h + 2*mm
# step-by-step roadmaps summary
ly = section_label(pm, LX, ly, COL_W, "▶ PROBLEM-SOLVING ROADMAPS", BG_HOWTO, CLR_HOWTO)
roadmaps = [
"PHOTON: λ(nm)→m → ν=c/λ → E=hν",
"ELECTRON CONFIG: [noble gas] → fill 4s²3d¹⁰4p² order",
"NAME IONIC: metal + (Roman# if variable) + anion name",
"WRITE FORMULA: crossover charges as subscripts, reduce",
"MOLE CALC: find MM first → ÷MM for moles → ×MM for grams",
"% COMP: (elem mass / MM) × 100 for each element",
"EMPIRICAL: %→g ÷ at.mass → ÷ smallest → whole # ratio",
"STOICH: balance → to mol → molar ratio → to units",
"LIMITING R: both→mol → both make product → smaller = LR",
"% YIELD: actual ÷ theoretical × 100",
"HEAT: q = m × c × ΔT (ΔT = Tf − Ti)",
]
ly = text_block(pm, LX, ly, COL_W, roadmaps, bg=BG_HOWTO, border=CLR_HOWTO,
size=6.5, line_h=8.5, color=BLACK)
# RIGHT col: polyatomic ions table + memory tricks + bonding table + spontaneity
ry = section_label(pm, RX, ry, COL_W, "▶ POLYATOMIC IONS — COMPLETE LIST", BG_THEORY, CLR_THEORY)
ry = table_block(pm, RX, ry, COL_W,
["Ion", "Name", "Ion", "Name"],
[
["SO₄²⁻","sulfate", "ClO⁻", "hypochlorite"],
["SO₃²⁻","sulfite", "ClO₂⁻", "chlorite"],
["NO₃⁻", "nitrate", "ClO₃⁻", "chlorate"],
["NO₂⁻", "nitrite", "ClO₄⁻", "perchlorate"],
["PO₄³⁻","phosphate", "OH⁻", "hydroxide"],
["NH₄⁺", "ammonium", "CO₃²⁻", "carbonate"],
["CrO₄²⁻","chromate", "MnO₄⁻", "permanganate"],
],
col_ratios=[0.20, 0.30, 0.20, 0.30]
)
ry = section_label(pm, RX, ry, COL_W, "▶ BOND & CHARGE QUICK REFERENCE", BG_THEORY, CLR_THEORY)
ry = table_block(pm, RX, ry, COL_W,
["Bond", "σ bonds", "π bonds", "Strength", "Length"],
[
["Single", "1", "0", "weakest", "longest"],
["Double", "1", "1", "medium", "medium"],
["Triple", "1", "2", "strongest","shortest"],
],
col_ratios=[0.20, 0.18, 0.18, 0.22, 0.22]
)
ry = section_label(pm, RX, ry, COL_W, "▶ COMMON ION CHARGES", BG_THEORY, CLR_THEORY)
ry = table_block(pm, RX, ry, COL_W,
["Group", "Charge", "Examples"],
[
["1 (alkali metals)", "1+", "Li, Na, K, Rb, Cs"],
["2 (alkaline earth)", "2+", "Be, Mg, Ca, Sr, Ba"],
["13 (Al group)", "3+", "Al, Ga"],
["15 (N group)", "3−", "N, P, As"],
["16 (O group)", "2−", "O, S, Se, Te"],
["17 (halogens)", "1−", "F, Cl, Br, I"],
["18 (noble gases)", "0", "He, Ne, Ar, Kr"],
["Transition metals", "var","Fe(2+/3+) Cu(1+/2+)"],
],
col_ratios=[0.38, 0.15, 0.47]
)
ry = section_label(pm, RX, ry, COL_W, "▶ SPONTANEITY SUMMARY", BG_THEORY, CLR_THEORY)
ry = table_block(pm, RX, ry, COL_W,
["ΔH", "ΔS", "Spontaneous?"],
[
["< 0", "> 0", "ALWAYS (any T)"],
["< 0", "< 0", "Low T only"],
["> 0", "> 0", "High T only"],
["> 0", "< 0", "NEVER"],
],
col_ratios=[0.22, 0.22, 0.56]
)
ry = section_label(pm, RX, ry, COL_W, "▶ GREEK PREFIXES FOR MOLECULAR COMPOUNDS", BG_THEORY, CLR_THEORY)
ry = table_block(pm, RX, ry, COL_W,
["#", "Prefix", "#", "Prefix", "#", "Prefix"],
[
["1","mono", "4","tetra","7","hepta"],
["2","di", "5","penta","8","octa"],
["3","tri", "6","hexa", "9","nona"],
],
col_ratios=[0.08, 0.17, 0.08, 0.17, 0.08, 0.17]
)
pm.save()
cd /home/daytona/workspace/chem-cheatsheet && python3 studyguide_complete.py 2>&1
~/chem-cheatsheet/cheatsheet_final.py
"""
Honors Chemistry — CHEAT SHEET (2 pages, A4)
Page 1: Periodic Table + full legend (metal/nonmetal/charges/polyatomic ions)
Page 2: Theory + How-To steps for ALL 9 topics. NO worked examples.
Designed to pack maximum useful content into one A4 sheet (print double-sided).
"""
from reportlab.lib.pagesizes import A4
from reportlab.lib.units import cm, mm
from reportlab.lib import colors
from reportlab.pdfgen import canvas as pdfcanvas
W, H = A4
OUT = "/home/daytona/workspace/chem-cheatsheet/HonorsChem_CheatSheet_Final.pdf"
# ── Palette ────────────────────────────────────────────────────────
P = {
"hdr": colors.HexColor("#0d1b4b"),
"hdr2": colors.HexColor("#1a3a7c"),
"topic1": colors.HexColor("#0d47a1"), # atomic
"topic2": colors.HexColor("#4a148c"), # bonding
"topic3": colors.HexColor("#b45309"), # nomenclature
"topic4": colors.HexColor("#991b1b"), # reactions
"topic5": colors.HexColor("#065f46"), # mole
"topic6": colors.HexColor("#7c2d12"), # stoich
"topic7": colors.HexColor("#164e63"), # thermo
"topic8": colors.HexColor("#14532d"), # kinetics
"topic9": colors.HexColor("#374151"), # equilibrium
"thy_bg": colors.HexColor("#eff6ff"),
"thy_bd": colors.HexColor("#3b82f6"),
"how_bg": colors.HexColor("#f0fdf4"),
"how_bd": colors.HexColor("#16a34a"),
"warn_bg": colors.HexColor("#fff7ed"),
"warn_bd": colors.HexColor("#ea580c"),
"tbl_hdr": colors.HexColor("#334155"),
"tbl_alt": colors.HexColor("#f1f5f9"),
# periodic table categories
"alkali": colors.HexColor("#fca5a5"),
"alkaline": colors.HexColor("#fcd34d"),
"transition":colors.HexColor("#93c5fd"),
"postmetal": colors.HexColor("#86efac"),
"metalloid": colors.HexColor("#c4b5fd"),
"nonmetal": colors.HexColor("#fef08a"),
"halogen": colors.HexColor("#67e8f9"),
"noble": colors.HexColor("#f9a8d4"),
"lan": colors.HexColor("#d6b4a0"),
"act": colors.HexColor("#b0c4de"),
"white": colors.white,
"black": colors.black,
"grey": colors.HexColor("#374151"),
"lgrey": colors.HexColor("#e5e7eb"),
"border": colors.HexColor("#94a3b8"),
}
c = pdfcanvas.Canvas(OUT, pagesize=A4)
# ══════════════════════════════════════════════════════════════════
# HELPER FUNCTIONS
# ══════════════════════════════════════════════════════════════════
def hdr_bar(y, text, h=15):
c.setFillColor(P["hdr"])
c.rect(0, y - h, W, h, fill=1, stroke=0)
c.setFillColor(P["white"])
c.setFont("Helvetica-Bold", 8.5)
c.drawCentredString(W/2, y - h + 3.5, text)
return y - h
def section_bar_full(x, y, w, text, color, h=10):
c.setFillColor(color)
c.rect(x, y - h, w, h, fill=1, stroke=0)
c.setFillColor(P["white"])
c.setFont("Helvetica-Bold", 6.5)
c.drawString(x + 3, y - h + 2.5, text)
return y - h
def mini_table(x, y, w, headers, rows, col_ratios, size=5.8, rh=8):
col_widths = [w * r for r in col_ratios]
# header
c.setFillColor(P["tbl_hdr"])
c.rect(x, y - rh, w, rh, fill=1, stroke=0)
c.setFillColor(P["white"])
c.setFont("Helvetica-Bold", size)
cx = x
for h_txt, cw in zip(headers, col_widths):
c.drawString(cx + 2, y - rh + 2, h_txt)
cx += cw
# rows
for ri, row in enumerate(rows):
ry2 = y - rh * (ri + 2)
c.setFillColor(P["tbl_alt"] if ri % 2 == 0 else P["white"])
c.rect(x, ry2, w, rh, fill=1, stroke=0)
c.setFillColor(P["black"])
c.setFont("Helvetica", size)
cx = x
for cell, cw in zip(row, col_widths):
c.drawString(cx + 2, ry2 + 2, str(cell))
cx += cw
c.setStrokeColor(P["border"])
c.setLineWidth(0.3)
c.rect(x, y - rh * (len(rows) + 1), w, rh * (len(rows) + 1), fill=0, stroke=1)
return y - rh * (len(rows) + 1) - 1.5*mm
def theory_box(x, y, w, lines, lh=7.5, pad=2.5):
total_h = len(lines) * lh + 2 * pad
c.setFillColor(P["thy_bg"])
c.setStrokeColor(P["thy_bd"])
c.setLineWidth(0.4)
c.rect(x, y - total_h, w, total_h, fill=1, stroke=1)
c.setFillColor(P["grey"])
c.setFont("Helvetica", 6)
for i, ln in enumerate(lines):
c.drawString(x + 3, y - pad - i * lh - 5, ln)
return y - total_h - 1*mm
def howto_box(x, y, w, steps, lh=7.5, pad=2.5):
total_h = len(steps) * lh + 2 * pad
c.setFillColor(P["how_bg"])
c.setStrokeColor(P["how_bd"])
c.setLineWidth(0.5)
c.rect(x, y - total_h, w, total_h, fill=1, stroke=1)
for i, step in enumerate(steps):
c.setFillColor(P["how_bd"])
c.setFont("Helvetica-Bold", 6)
c.drawString(x + 3, y - pad - i * lh - 5, f"{i+1}.")
c.setFillColor(P["black"])
c.setFont("Helvetica", 6)
c.drawString(x + 12, y - pad - i * lh - 5, step)
return y - total_h - 1*mm
def warn_line(x, y, w, text):
c.setFillColor(P["warn_bg"])
c.setStrokeColor(P["warn_bd"])
c.setLineWidth(0.4)
c.rect(x, y - 9, w, 9, fill=1, stroke=1)
c.setFillColor(P["warn_bd"])
c.setFont("Helvetica-Bold", 5.5)
c.drawString(x + 2, y - 6.5, "⚠")
c.setFillColor(P["black"])
c.setFont("Helvetica", 5.8)
c.drawString(x + 10, y - 6.5, text)
return y - 10
def topic_bar(x, y, w, num, title, qs, color, h=11):
c.setFillColor(color)
c.roundRect(x, y - h, w, h, 2, fill=1, stroke=0)
c.setFillColor(P["white"])
c.setFont("Helvetica-Bold", 7)
c.drawString(x + 3, y - h + 2.5, f"T{num}: {title}")
c.setFont("Helvetica", 6)
c.drawRightString(x + w - 3, y - h + 2.5, f"Qs {qs}")
return y - h - 1*mm
# ══════════════════════════════════════════════════════════════════
# PAGE 1 — PERIODIC TABLE + LEGEND
# ══════════════════════════════════════════════════════════════════
margin = 5*mm
y = H - margin
y = hdr_bar(y, "HONORS CHEMISTRY — CHEAT SHEET · Page 1: Periodic Table Reference · Pendleton High School", h=14)
y -= 2*mm
# ── PERIODIC TABLE ─────────────────────────────────────────────────
table_h_area = H * 0.465
n_rows = 9 # 7 periods + gap + 2 lan/act
n_cols = 18
cw = (W - 2*margin) / n_cols
ch = table_h_area / (n_rows + 0.3)
ELEM = {}
raw = [
# (period, group, symbol, Z, cat, charge)
(1,1,"H",1,"nonmetal","1+/−"),(1,18,"He",2,"noble","0"),
(2,1,"Li",3,"alkali","1+"),(2,2,"Be",4,"alkaline","2+"),
(2,13,"B",5,"metalloid","3+"),(2,14,"C",6,"nonmetal","4−"),
(2,15,"N",7,"nonmetal","3−"),(2,16,"O",8,"nonmetal","2−"),
(2,17,"F",9,"halogen","1−"),(2,18,"Ne",10,"noble","0"),
(3,1,"Na",11,"alkali","1+"),(3,2,"Mg",12,"alkaline","2+"),
(3,13,"Al",13,"postmetal","3+"),(3,14,"Si",14,"metalloid","4−"),
(3,15,"P",15,"nonmetal","3−"),(3,16,"S",16,"nonmetal","2−"),
(3,17,"Cl",17,"halogen","1−"),(3,18,"Ar",18,"noble","0"),
(4,1,"K",19,"alkali","1+"),(4,2,"Ca",20,"alkaline","2+"),
(4,3,"Sc",21,"transition","3+"),(4,4,"Ti",22,"transition","4+"),
(4,5,"V",23,"transition","var"),(4,6,"Cr",24,"transition","var"),
(4,7,"Mn",25,"transition","var"),(4,8,"Fe",26,"transition","var"),
(4,9,"Co",27,"transition","var"),(4,10,"Ni",28,"transition","var"),
(4,11,"Cu",29,"transition","var"),(4,12,"Zn",30,"transition","2+"),
(4,13,"Ga",31,"postmetal","3+"),(4,14,"Ge",32,"metalloid","4−"),
(4,15,"As",33,"metalloid","3−"),(4,16,"Se",34,"nonmetal","2−"),
(4,17,"Br",35,"halogen","1−"),(4,18,"Kr",36,"noble","0"),
(5,1,"Rb",37,"alkali","1+"),(5,2,"Sr",38,"alkaline","2+"),
(5,3,"Y",39,"transition","3+"),(5,4,"Zr",40,"transition","4+"),
(5,5,"Nb",41,"transition","var"),(5,6,"Mo",42,"transition","var"),
(5,7,"Tc",43,"transition","var"),(5,8,"Ru",44,"transition","var"),
(5,9,"Rh",45,"transition","var"),(5,10,"Pd",46,"transition","var"),
(5,11,"Ag",47,"transition","1+"),(5,12,"Cd",48,"transition","2+"),
(5,13,"In",49,"postmetal","3+"),(5,14,"Sn",50,"postmetal","var"),
(5,15,"Sb",51,"metalloid","3−"),(5,16,"Te",52,"metalloid","2−"),
(5,17,"I",53,"halogen","1−"),(5,18,"Xe",54,"noble","0"),
(6,1,"Cs",55,"alkali","1+"),(6,2,"Ba",56,"alkaline","2+"),
(6,3,"La*",57,"lan","3+"),(6,4,"Hf",72,"transition","4+"),
(6,5,"Ta",73,"transition","5+"),(6,6,"W",74,"transition","var"),
(6,7,"Re",75,"transition","var"),(6,8,"Os",76,"transition","var"),
(6,9,"Ir",77,"transition","var"),(6,10,"Pt",78,"transition","var"),
(6,11,"Au",79,"transition","var"),(6,12,"Hg",80,"transition","var"),
(6,13,"Tl",81,"postmetal","var"),(6,14,"Pb",82,"postmetal","var"),
(6,15,"Bi",83,"postmetal","3+"),(6,16,"Po",84,"metalloid","2−"),
(6,17,"At",85,"halogen","1−"),(6,18,"Rn",86,"noble","0"),
(7,1,"Fr",87,"alkali","1+"),(7,2,"Ra",88,"alkaline","2+"),
(7,3,"Ac*",89,"act","3+"),
]
cat_color = {
"alkali":P["alkali"],"alkaline":P["alkaline"],"transition":P["transition"],
"postmetal":P["postmetal"],"metalloid":P["metalloid"],"nonmetal":P["nonmetal"],
"halogen":P["halogen"],"noble":P["noble"],"lan":P["lan"],"act":P["act"],
}
def draw_cell(period, group, sym, Z, cat, charge, x_extra=0, y_extra=0):
col = group - 1
row = period - 1
x = margin + col * cw + x_extra
ey = y - (row + 1) * ch + y_extra
bg = cat_color.get(cat, P["lgrey"])
c.setFillColor(bg)
c.setStrokeColor(P["border"])
c.setLineWidth(0.25)
c.rect(x + 0.3, ey + 0.3, cw - 0.6, ch - 0.6, fill=1, stroke=1)
# atomic number
c.setFillColor(colors.HexColor("#475569"))
c.setFont("Helvetica", 3.8)
c.drawString(x + 1.5, ey + ch - 5, str(Z))
# symbol
c.setFillColor(P["black"])
fsz = 7 if len(sym) <= 2 else 5.5
c.setFont("Helvetica-Bold", fsz)
c.drawCentredString(x + cw/2, ey + ch/2 - 2.5, sym)
# charge
c.setFillColor(colors.HexColor("#dc2626"))
c.setFont("Helvetica", 3.5)
c.drawCentredString(x + cw/2, ey + 1.5, charge)
# Group labels
for g in range(1, 19):
gx = margin + (g - 1) * cw + cw/2
c.setFillColor(P["hdr2"])
c.setFont("Helvetica-Bold", 5)
c.drawCentredString(gx, y + 0.5*mm, str(g))
# Period labels
for p in range(1, 8):
py2 = y - p * ch + ch/2 - 2
c.setFillColor(P["hdr2"])
c.setFont("Helvetica-Bold", 5.5)
c.drawRightString(margin - 1, py2, str(p))
# Draw main table
for (period, group, sym, Z, cat, charge) in raw:
draw_cell(period, group, sym, Z, cat, charge)
# Lanthanides / Actinides
lan_data = [
("La",57,"lan","3+"),("Ce",58,"lan","3+"),("Pr",59,"lan","3+"),
("Nd",60,"lan","3+"),("Pm",61,"lan","var"),("Sm",62,"lan","3+"),
("Eu",63,"lan","3+"),("Gd",64,"lan","3+"),("Tb",65,"lan","3+"),
("Dy",66,"lan","3+"),("Ho",67,"lan","3+"),("Er",68,"lan","3+"),
("Tm",69,"lan","3+"),("Yb",70,"lan","2+"),("Lu",71,"lan","3+"),
]
act_data = [
("Ac",89,"act","3+"),("Th",90,"act","4+"),("Pa",91,"act","5+"),
("U",92,"act","var"),("Np",93,"act","var"),("Pu",94,"act","var"),
("Am",95,"act","3+"),("Cm",96,"act","3+"),("Bk",97,"act","3+"),
("Cf",98,"act","3+"),("Es",99,"act","3+"),("Fm",100,"act","3+"),
("Md",101,"act","3+"),("No",102,"act","2+"),("Lr",103,"act","3+"),
]
lan_y_off = -1.35 * ch
act_y_off = -2.1 * ch
for i, (sym, Z, cat, charge) in enumerate(lan_data):
col = i + 3
row = 7
x = margin + col * cw
ey = y - (row + 1) * ch + lan_y_off
bg = cat_color.get(cat, P["lgrey"])
c.setFillColor(bg)
c.setStrokeColor(P["border"])
c.setLineWidth(0.25)
c.rect(x + 0.3, ey + 0.3, cw - 0.6, ch - 0.6, fill=1, stroke=1)
c.setFillColor(P["black"])
c.setFont("Helvetica-Bold", 6.5)
c.drawCentredString(x + cw/2, ey + ch/2 - 2.5, sym)
c.setFillColor(colors.HexColor("#dc2626"))
c.setFont("Helvetica", 3.5)
c.drawCentredString(x + cw/2, ey + 1.5, charge)
for i, (sym, Z, cat, charge) in enumerate(act_data):
col = i + 3
row = 8
x = margin + col * cw
ey = y - (row + 1) * ch + act_y_off
bg = cat_color.get(cat, P["lgrey"])
c.setFillColor(bg)
c.setStrokeColor(P["border"])
c.setLineWidth(0.25)
c.rect(x + 0.3, ey + 0.3, cw - 0.6, ch - 0.6, fill=1, stroke=1)
c.setFillColor(P["black"])
c.setFont("Helvetica-Bold", 6.5)
c.drawCentredString(x + cw/2, ey + ch/2 - 2.5, sym)
c.setFillColor(colors.HexColor("#dc2626"))
c.setFont("Helvetica", 3.5)
c.drawCentredString(x + cw/2, ey + 1.5, charge)
# Figure out where table ends
table_bottom = y - 9 * ch + act_y_off - 3*mm
# ── LEGEND ─────────────────────────────────────────────────────────
legend_y = table_bottom - 2*mm
c.setFillColor(P["hdr"])
c.rect(margin, legend_y - 11, W - 2*margin, 11, fill=1, stroke=0)
c.setFillColor(P["white"])
c.setFont("Helvetica-Bold", 7)
c.drawCentredString(W/2, legend_y - 8, "PERIODIC TABLE KEY — Colors, Charges, and Special Groups")
legend_y -= 13
# Category swatches (2 rows × 4)
cats = [
("Alkali Metals Gp1", "alkali", "1+ charge (Li,Na,K)"),
("Alkaline Earth Gp2","alkaline", "2+ charge (Mg,Ca,Ba)"),
("Transition Metals", "transition", "Variable charge (Fe,Cu,Cr)"),
("Post-Trans. Metals","postmetal", "Al(3+),Pb,Sn,Tl var"),
("Metalloids", "metalloid", "Staircase line: B,Si,Ge,As"),
("Nonmetals", "nonmetal", "Neg ions (O²⁻,N³⁻,S²⁻)"),
("Halogens Gp17", "halogen", "1− charge (F,Cl,Br,I)"),
("Noble Gases Gp18", "noble", "0 charge — never bond"),
]
bw = (W - 2*margin) / 4
bh = 18
for i, (name, cat, desc) in enumerate(cats):
col = i % 4
row = i // 4
bx = margin + col * bw
by = legend_y - row * (bh + 2)
c.setFillColor(cat_color[cat])
c.setStrokeColor(P["border"])
c.setLineWidth(0.4)
c.rect(bx, by - bh, bw - 2, bh, fill=1, stroke=1)
c.setFillColor(P["black"])
c.setFont("Helvetica-Bold", 6)
c.drawString(bx + 2, by - 8, name)
c.setFillColor(P["grey"])
c.setFont("Helvetica", 5.5)
c.drawString(bx + 2, by - 15, desc)
legend_y -= 2 * (bh + 2) + 2*mm
# Metal / nonmetal divider note
c.setFillColor(P["hdr2"])
c.setFont("Helvetica-Bold", 6.5)
c.drawString(margin, legend_y, "METALS (left of staircase line) ←→ METALLOIDS (on staircase) ←→ NONMETALS (right of staircase)")
legend_y -= 6*mm
# Ion charges table
c.setFillColor(P["hdr"])
c.rect(margin, legend_y - 10, W - 2*margin, 10, fill=1, stroke=0)
c.setFillColor(P["white"])
c.setFont("Helvetica-Bold", 7)
c.drawCentredString(W/2, legend_y - 7.5, "COMMON ION CHARGES — What charge does each group form?")
legend_y -= 12
charge_data = [
("Group 1","1+","Li,Na,K,Rb,Cs"),
("Group 2","2+","Be,Mg,Ca,Sr,Ba"),
("Group 13","3+","Al,Ga,In"),
("Group 15","3−","N,P,As"),
("Group 16","2−","O,S,Se,Te"),
("Group 17","1−","F,Cl,Br,I"),
("Group 18","0","He,Ne,Ar,Kr"),
("Transition","VAR","Fe,Cu,Cr,Mn,Pb"),
]
ccw = (W - 2*margin) / 8
cch = 20
for i, (grp, chg, ex) in enumerate(charge_data):
cx2 = margin + i * ccw
bg = P["alkali"] if i == 0 else P["alkaline"] if i == 1 else P["postmetal"] if i == 2 else \
P["nonmetal"] if i < 6 else P["noble"] if i == 6 else P["transition"]
c.setFillColor(bg)
c.setStrokeColor(P["border"])
c.setLineWidth(0.4)
c.rect(cx2, legend_y - cch, ccw - 1, cch, fill=1, stroke=1)
c.setFillColor(P["black"])
c.setFont("Helvetica-Bold", 6)
c.drawCentredString(cx2 + ccw/2, legend_y - 8, grp)
c.setFillColor(P["hdr"])
c.setFont("Helvetica-Bold", 9)
c.drawCentredString(cx2 + ccw/2, legend_y - 16, chg)
legend_y -= cch + 1*mm
# examples row
c.setFillColor(P["grey"])
c.setFont("Helvetica", 5.5)
for i, (grp, chg, ex) in enumerate(charge_data):
cx2 = margin + i * ccw + ccw/2
c.drawCentredString(cx2, legend_y, ex)
legend_y -= 6*mm
# Special elements + polyatomic ions in two columns
half = (W - 2*margin - 3*mm) / 2
lx2 = margin
rx2 = margin + half + 3*mm
# LEFT: special elements
c.setFillColor(colors.HexColor("#dbeafe"))
c.setStrokeColor(P["topic1"])
c.setLineWidth(0.5)
spec_h = 36
c.rect(lx2, legend_y - spec_h, half, spec_h, fill=1, stroke=1)
c.setFillColor(P["hdr"])
c.setFont("Helvetica-Bold", 6.5)
c.drawString(lx2 + 3, legend_y - 9, "★ DIATOMIC (HOFBrINCl) — always paired:")
c.setFillColor(P["black"])
c.setFont("Helvetica", 6.5)
c.drawString(lx2 + 3, legend_y - 17, "H₂ O₂ F₂ Br₂ I₂ N₂ Cl₂")
c.setFillColor(P["hdr"])
c.setFont("Helvetica-Bold", 6.5)
c.drawString(lx2 + 3, legend_y - 26, "★ NOBLE GASES — monoatomic, never bond:")
c.setFillColor(P["black"])
c.setFont("Helvetica", 6.5)
c.drawString(lx2 + 3, legend_y - 34, "He Ne Ar Kr Xe Rn (write alone, never as He₂!)")
# RIGHT: variable charge metals
c.setFillColor(colors.HexColor("#fef3c7"))
c.setStrokeColor(P["warn_bd"])
c.setLineWidth(0.5)
c.rect(rx2, legend_y - spec_h, half, spec_h, fill=1, stroke=1)
c.setFillColor(P["warn_bd"])
c.setFont("Helvetica-Bold", 6.5)
c.drawString(rx2 + 3, legend_y - 9, "★ VARIABLE-CHARGE METALS (need Roman numerals):")
c.setFillColor(P["black"])
c.setFont("Helvetica", 6.3)
c.drawString(rx2 + 3, legend_y - 17, "Fe: 2+ (iron II) or 3+ (iron III)")
c.drawString(rx2 + 3, legend_y - 24, "Cu: 1+ (copper I) or 2+ (copper II)")
c.drawString(rx2 + 3, legend_y - 31, "Pb: 2+ or 4+ Sn: 2+ or 4+ Hg: 1+ or 2+")
legend_y -= spec_h + 2*mm
# Polyatomic ions — full width, compact
c.setFillColor(colors.HexColor("#4a148c"))
c.rect(margin, legend_y - 10, W - 2*margin, 10, fill=1, stroke=0)
c.setFillColor(P["white"])
c.setFont("Helvetica-Bold", 7)
c.drawCentredString(W/2, legend_y - 7.5, "POLYATOMIC IONS (memorize all of these!)")
legend_y -= 12
ions = [
("SO₄²⁻","sulfate"),("SO₃²⁻","sulfite"),("NO₃⁻","nitrate"),
("NO₂⁻","nitrite"),("PO₄³⁻","phosphate"),("OH⁻","hydroxide"),
("NH₄⁺","ammonium"),("CO₃²⁻","carbonate"),("ClO⁻","hypochlorite"),
("ClO₂⁻","chlorite"),("ClO₃⁻","chlorate"),("ClO₄⁻","perchlorate"),
]
icw = (W - 2*margin) / 6
irh = 10
for i, (ion, name) in enumerate(ions):
col = i % 6
row = i // 6
ix = margin + col * icw
iy = legend_y - row * irh
ibg = colors.HexColor("#ede7f6") if col % 2 == 0 else colors.HexColor("#f3e5f5")
c.setFillColor(ibg)
c.setStrokeColor(colors.HexColor("#7b1fa2"))
c.setLineWidth(0.3)
c.rect(ix, iy - irh, icw - 0.5, irh, fill=1, stroke=1)
c.setFillColor(colors.HexColor("#4a148c"))
c.setFont("Helvetica-Bold", 6.5)
c.drawString(ix + 2, iy - irh + 2.5, ion)
c.setFillColor(P["grey"])
c.setFont("Helvetica", 6)
c.drawString(ix + 28, iy - irh + 2.5, name)
legend_y -= 2 * irh + 2*mm
# Memory trick strip
c.setFillColor(colors.HexColor("#fff7ed"))
c.setStrokeColor(P["warn_bd"])
c.setLineWidth(0.5)
trick_h = 14
c.rect(margin, legend_y - trick_h, W - 2*margin, trick_h, fill=1, stroke=1)
c.setFillColor(P["warn_bd"])
c.setFont("Helvetica-Bold", 6)
c.drawString(margin + 3, legend_y - 8, "MEMORY TRICKS:")
c.setFillColor(P["black"])
c.setFont("Helvetica", 6)
c.drawString(margin + 75, legend_y - 8,
"-ate = MORE oxygens | -ite = FEWER oxygens | hypo- = fewest O | per- = most O")
c.drawString(margin + 3, legend_y - trick_h + 2,
"Electron config fill order: 1s 2s 2p 3s 3p 4s 3d 4p 5s 4d 5p ... (4s BEFORE 3d!)")
# Footer
c.setFillColor(P["hdr"])
c.rect(0, 0, W, 10, fill=1, stroke=0)
c.setFillColor(P["white"])
c.setFont("Helvetica", 5.5)
c.drawCentredString(W/2, 3, "Page 1 of 2 · Flip for Step-by-Step Problem Solving → Pendleton High School · Honors Chemistry Final Exam")
# ══════════════════════════════════════════════════════════════════
# PAGE 2 — THEORY + HOW-TO (NO EXAMPLES)
# ══════════════════════════════════════════════════════════════════
c.showPage()
c.setPageSize(A4)
# light bg
c.setFillColor(colors.HexColor("#f8fafc"))
c.rect(0, 0, W, H, fill=1, stroke=0)
y = H - margin
y = hdr_bar(y, "PAGE 2: THEORY + HOW-TO STEPS FOR ALL 9 TOPICS · No examples — study those separately", h=14)
y -= 2*mm
MARGIN2 = margin
COL = (W - 2*MARGIN2 - 3*mm) / 2
LX = MARGIN2
RX = MARGIN2 + COL + 3*mm
ly = y
ry = y
# ── TOPIC 1: Atomic Structure ──────────────────────────────────────
ly = topic_bar(LX, ly, COL, 1, "ATOMIC STRUCTURE & PERIODIC TABLE", "44–50", P["topic1"])
ly = theory_box(LX, ly, COL, [
"Ground state = lowest energy; Excited = electron absorbed energy, jumped up",
"Isotopes = same element, diff neutrons → diff mass (e.g. C-12 vs C-14)",
"Allotropes = same element, diff structure (diamond/graphite = both carbon)",
"Alloy = metallic mixture (steel=Fe+C, bronze=Cu+Sn)",
"Octet rule: atoms are stable with 8 electrons in outer shell",
"Fill order: 1s 2s 2p 3s 3p [4s 3d] 4p 5s 4d 5p ... (4s BEFORE 3d!)",
])
ly = howto_box(LX, ly, COL, [
"Electron config: find Z, find prev. noble gas, write [NG]",
"Continue filling: 4s² → 3d¹⁰ → 4p² (32−18=14 e⁻ for Ge)",
"Ge (Z=32): [Ar] 4s² 3d¹⁰ 4p²",
"Light: ν = c/λ then E = hν (λ MUST be in meters!)",
"c=3.00×10⁸ m/s h=6.626×10⁻³⁴ J·s",
])
# ── TOPIC 2: Chemical Bonding ──────────────────────────────────────
ly = topic_bar(LX, ly, COL, 2, "CHEMICAL BONDING", "32–40", P["topic2"])
ly = theory_box(LX, ly, COL, [
"Ionic: Metal GIVES e⁻ to nonmetal → forms crystals, HIGH melting point",
"Covalent: Nonmetal+Nonmetal SHARE e⁻",
"Coordinate covalent: ONE atom donates BOTH electrons (e.g. NH₄⁺)",
"Single=1σ,0π | Double=1σ,1π | Triple=1σ,2π",
"Shorter bond = Stronger = Higher energy to break",
"Nonpolar: symmetric (SiH₄ tetrahedral) | Polar: has lone pairs (H₂S,PH₃)",
"HOFBrINCl: 7 diatomic elements H₂ O₂ F₂ Br₂ I₂ N₂ Cl₂",
])
ly = howto_box(LX, ly, COL, [
"Count σ/π: every bond has 1σ; double adds 1π; triple adds 2π",
"C₂H₄: C=C(1σ+1π) + 4 C-H(4σ) = 5σ total, 1π total",
"Polarity: lone pairs on central atom → polar molecule",
"Bond type from formula: = is double, ≡ is triple",
])
# ── TOPIC 3: Nomenclature ─────────────────────────────────────────
ly = topic_bar(LX, ly, COL, 3, "NOMENCLATURE & FORMULAS", "27,35,36,41–43", P["topic3"])
ly = theory_box(LX, ly, COL, [
"Ionic naming: metal name + (Roman# if variable charge) + anion name",
"Variable charge metals: Fe, Cu, Pb, Sn, Hg, Cr, Mn",
"-ate = more O | -ite = fewer O | hypo=fewest | per=most",
"Hydrate: Compound•xH₂O (e.g. Ba(OH)₂•8H₂O = octahydrate)",
"Molecular: Greek prefixes (di-N₂, tetra-4, hexa-6, octa-8...)",
"Acids: -ate ion → -ic acid (H₂SO₄=sulfuric) | -ite → -ous (H₂SO₃=sulfurous)",
])
ly = howto_box(LX, ly, COL, [
"Name ionic: identify metal, check if variable charge",
"Variable? Count total negative charge ÷ # metal atoms = charge",
"Write Roman numeral in (): Fe₂(SO₄)₃ → 3×2−=6− → Fe=3+ → Iron(III) sulfate",
"Write formula: CROSSOVER method — swap charges as subscripts",
"Ca²⁺+PO₄³⁻ → Ca₃(PO₄)₂ | reduce if both even (Ca₂O₂→CaO)",
])
ly = warn_line(LX, ly, COL, "Polyatomic with subscript > 1 MUST have parentheses: (PO₄)₂ not PO₄₂")
# ── TOPIC 4: Reactions ────────────────────────────────────────────
ly = topic_bar(LX, ly, COL, 4, "CHEMICAL REACTIONS & EQUATIONS", "28–31", P["topic4"])
ly = theory_box(LX, ly, COL, [
"Conservation of Mass: same atoms on both sides — only coefficients change",
"Precipitate: solid (s) product from 2 aq. solutions (e.g. AgCl(s))",
"Spectator ions: appear UNCHANGED on both sides → remove for net ionic",
"Neutralization: Acid + Base → Salt + Water",
"Net ionic for any neutralization: H⁺(aq) + OH⁻(aq) → H₂O(l)",
])
ly = howto_box(LX, ly, COL, [
"Balance: count each element, add coefficients (NOT subscripts)",
"Balance metals first, then nonmetals, then H, then O last",
"Net ionic: split all (aq), keep (s)(l)(g) whole, cancel spectators",
"SiO₂+4HF→SiF₄+2H₂O (coeff of HF=4)",
"Neutralization: Ba(OH)₂+2HCl→BaCl₂+2H₂O",
])
# RIGHT COLUMN starts here
# ── TOPIC 5: Mole ─────────────────────────────────────────────────
ry = topic_bar(RX, ry, COL, 5, "THE MOLE & MOLAR MASS", "19–26", P["topic5"])
ry = theory_box(RX, ry, COL, [
"1 mole = 6.02×10²³ particles (Avogadro's number)",
"Molar mass = grams per mole of a substance (from periodic table)",
"Compound MM: add atomic masses × subscripts for each element",
"Stoichiometry based on Law of Conservation of Mass",
"Conversions: g→mol (÷MM) | mol→g (×MM) | mol→particles (×Avogadro)",
])
ry = howto_box(RX, ry, COL, [
"Step 1: Calculate molar mass (add all atomic masses in formula)",
"Grams → Moles: n = mass ÷ molar mass",
"Moles → Grams: g = moles × molar mass",
"Moles → Particles: N = moles × 6.02×10²³",
"% comp: % = (elem. mass in formula / MM) × 100 → must sum to 100",
"Empirical: %→g → ÷at.mass → ÷smallest → whole # ratio",
])
ry = warn_line(RX, ry, COL, "H₂CrO₄: MM=2(1)+52+4(16)=118 | Cl₂: 71.0 | Fe₂O₃: 159.7 g/mol")
# ── TOPIC 6: Stoichiometry ────────────────────────────────────────
ry = topic_bar(RX, ry, COL, 6, "STOICHIOMETRY", "11–18", P["topic6"])
ry = theory_box(RX, ry, COL, [
"Coefficients in balanced equation = MOLAR RATIOS",
"Limiting reactant: runs out first, determines max product",
"Excess reactant: what's left over after reaction stops",
"Theoretical yield: max product from stoichiometry (perfect conditions)",
"Actual yield: what you really get in the lab (always ≤ theoretical)",
"% yield = (actual ÷ theoretical) × 100",
])
ry = howto_box(RX, ry, COL, [
"ALWAYS STEP 1: write and balance the equation",
"Stoich: given(g) ÷ MM → mol → × molar ratio → mol wanted → × MM",
"Limiting R: convert BOTH reactants to mol → each makes product",
" the one making LESS product = LIMITING REACTANT",
"% yield: actual ÷ theoretical × 100 (must be ≤ 100%)",
"Molar ratio C₃H₈+5O₂→3CO₂+4H₂O: O₂:CO₂ = 5:3",
])
# ── TOPIC 7: Thermochemistry ──────────────────────────────────────
ry = topic_bar(RX, ry, COL, 7, "THERMOCHEMISTRY", "7–9", P["topic7"])
ry = theory_box(RX, ry, COL, [
"q = m × c × ΔT (q=heat J, m=mass g, c=spec.heat J/g°C, ΔT=Tf−Ti)",
"Water: c=4.18 J/g°C | Aluminum: c=0.900 J/g°C",
"1 Cal (food) = 1 kcal = 4184 J",
"Spontaneous if ΔH<0 AND ΔS>0 → always spontaneous (any temp)",
"ΔG = ΔH − TΔS | ΔG<0 = spontaneous | ΔG>0 = non-spontaneous",
])
ry = mini_table(RX, ry, COL,
["ΔH", "ΔS", "Spontaneous?"],
[["<0","<0","Low T only"],["<0",">0","ALWAYS ✓"],
[">0","<0","NEVER ✗"],[">0",">0","High T only"]],
[0.22,0.22,0.56], size=5.8, rh=7.5
)
ry = howto_box(RX, ry, COL, [
"Heat: find m,c,Ti,Tf → ΔT=Tf−Ti → q=m×c×ΔT (answer in J)",
"Cal→J: multiply by 4184 | J→Cal: divide by 4184",
"Spontaneity: state both ΔH and ΔS signs, use table above",
])
# ── TOPIC 8: Kinetics ─────────────────────────────────────────────
ry = topic_bar(RX, ry, COL, 8, "REACTION KINETICS", "3–6", P["topic8"])
ry = theory_box(RX, ry, COL, [
"Collision theory: must (1) collide, (2) have ≥ activation energy, (3) right orientation",
"Activated complex: unstable high-E intermediate at energy maximum",
" → NOT a reactant, NOT a product — exists only momentarily",
"↑Temp → rate ↑ (faster particles, more energetic collisions)",
"Catalyst → rate ↑, Ea ↓ BUT ΔH (overall energy) UNCHANGED",
"↑Concentration or ↑Surface area → rate ↑ (more collisions)",
])
ry = howto_box(RX, ry, COL, [
"Rate question: identify which factor changed → state effect on rate",
"Catalyst question: rate INCREASES + Ea DECREASES + ΔH unchanged",
"Activated complex = at PEAK of energy diagram, unstable, not product",
"Temp up = rate up because MORE collisions have ENOUGH energy",
])
# ── TOPIC 9: Equilibrium ──────────────────────────────────────────
ry = topic_bar(RX, ry, COL, 9, "CHEMICAL EQUILIBRIUM", "1–2", P["topic9"])
ry = theory_box(RX, ry, COL, [
"Equilibrium: forward rate = reverse rate (BOTH still occurring!)",
"NOT stopped — still dynamic, but concentrations/pressure stay constant",
"Keq valid ONLY at ONE specific temperature",
"Only TEMPERATURE changes the VALUE of Keq",
"Concentration/pressure changes SHIFT equilibrium (not Keq value)",
"Le Chatelier: stress → system shifts to relieve it",
" Add reactant → shifts right | Add product → shifts left",
" ↑Pressure → shifts to fewer moles of gas",
])
ry = howto_box(RX, ry, COL, [
"What is equilibrium? Fwd=Rev rate, both occurring, conc. constant",
"Something added: identify it → state which direction eq. shifts",
"Does Keq change? Only if TEMPERATURE changes. Conc/pressure = no",
"Le Chat: add more A+B⇌C+D reactant → shifts → makes more C,D",
])
# ── BOTTOM STRIP: Formula quick-ref ───────────────────────────────
bottom_y = min(ly, ry) - 2*mm
c.setFillColor(P["hdr"])
c.rect(MARGIN2, bottom_y - 11, W - 2*MARGIN2, 11, fill=1, stroke=0)
c.setFillColor(P["white"])
c.setFont("Helvetica-Bold", 7)
c.drawCentredString(W/2, bottom_y - 8, "ALL FORMULAS IN ONE LINE: ν=c/λ · E=hν · q=mcΔT · 1Cal=4184J · n=mass/MM · %comp=(elem/MM)×100 · %yield=(actual/theoretical)×100 · Avogadro=6.02×10²³")
bottom_y -= 13
# Footer
c.setFillColor(P["hdr"])
c.rect(0, 0, W, 10, fill=1, stroke=0)
c.setFillColor(P["white"])
c.setFont("Helvetica", 5.5)
c.drawCentredString(W/2, 3, "Page 2 of 2 · Pendleton High School · Honors Chemistry Final Exam · Print double-sided on one A4 sheet")
c.save()
print(f"✅ Saved: {OUT}")
cd /home/daytona/workspace/chem-cheatsheet && python3 cheatsheet_final.py 2>&1
ls -lh /home/daytona/workspace/chem-cheatsheet/*.pdf
HonorsChem StudyGuide Complete
PDF Document · PDF
HonorsChem CheatSheet Final
PDF Document · PDF