Chapter 2: Hydrogen Bond Donors and Acceptors#

1. Introduction#

Hydrogen bonds are the workhorses of molecular recognition in drug discovery. These moderate-strength interactions (1-5 kcal/mol) govern how drugs bind to their protein targets, cross biological membranes, and dissolve in aqueous media.

Earlier on, you learn about pKa and ionizaton state of drugs. These directly impacts a drug’s hydrogen bonding capability. For example, a carboxylic acid (COOH) at pH < pKa remains neutral and acts as both an H-bond donor and acceptor, but at pH > pKa it loses its proton (COO⁻) and can only accept hydrogen bonds.


2. Key Concepts and Definitions#

  • Hydrogen Bond Donor (HBD): A molecule containing a hydrogen atom covalently bonded to a highly electronegative atom, specifically nitrogen (N) or oxygen (O). In drug discovery, the entire N-H or O-H group is considered the donor, as it “donates” the hydrogen to form the bond.

  • Hydrogen Bond Acceptor (HBA): An electronegative atom, specifically a nitrogen (N) or oxygen (O), that possesses at least one lone pair of electrons. This lone pair can form a hydrogen bond with a hydrogen from a donor group.


3. Main Content#

3.1 Identifying Hydrogen Bond Donors and Acceptors#

Hydrogen Bond Donor (HBD) are molecules or functional groups that supplies the hydrogen atom for the bond. It consists of: a hydrogen atom covalently bonded to a highly electronegative atom (usually Nitrogen, Oxygen, or Fluorine).

For example,

  • Hydroxyl groups (OH): Alcohols, phenols, carboxylic acids

  • Primary amines (NH₂): Aniline, aliphatic amines

  • Secondary amines (NH): Piperidines, indoles

  • Amides (CONH): Peptide bonds, sulfonamides

  • Thiols (SH): Cysteine-like groups (weak donors)

Hydrogen Bond Acceptor (HBA) are molecules or functional groups that receives the hydrogen. It is an electronegative atom (typically N, O, or F) that possesses at least one available lone pair of electrons.

For example,

  • Carbonyl oxygen (C=O): Ketones, esters, amides, carboxylic acids

  • Ether oxygen (C-O-C): Ethers, alcohols

  • Amine nitrogen: Primary, secondary, tertiary amines

  • Aromatic nitrogen: Pyridine, imidazole, quinoline

  • Nitro groups (NO₂): Strong acceptors

  • Fluorine: Weak acceptor (rarely forms strong H-bonds)

Hide code cell source

from IPython.display import display, HTML
import json

class FunctionalGroupInteractive:
    def __init__(self):
        self.functional_groups = [
            {'name': 'Alcohol (-OH)', 'hbd': True, 'hba': True},
            {'name': 'Primary Amine (-NH₂)', 'hbd': True, 'hba': True},
            {'name': 'Carbonyl (C=O)', 'hbd': False, 'hba': True},
            {'name': 'Ether (-O-)', 'hbd': False, 'hba': True},
            {'name': 'Carboxylic Acid (-COOH)', 'hbd': True, 'hba': True},
            {'name': 'Amide (-CONH-)', 'hbd': True, 'hba': True},
            {'name': 'Tertiary Amine (R₃N)', 'hbd': False, 'hba': True},
            {'name': 'Ester (-COO-)', 'hbd': False, 'hba': True},
            {'name': 'Phenol (Ar-OH)', 'hbd': True, 'hba': True},
            {'name': 'Pyridine N', 'hbd': False, 'hba': True},
            {'name': 'Nitrile (-C≡N)', 'hbd': False, 'hba': True},
            {'name': 'Methyl (CH₃-)', 'hbd': False, 'hba': False},
        ]
        
    def create_interface(self):
        correct_answers_json = json.dumps({fg['name']: {'hbd': fg['hbd'], 'hba': fg['hba']} for fg in self.functional_groups})

        html_code = f'''
        <!DOCTYPE html>
        <html>
        <head>
            <style>
                .quiz-container {{
                    font-family: 'Segoe UI', Arial, sans-serif;
                    max-width: 900px;
                    margin: 0 auto;
                    padding: 20px;
                    background: #fff;
                    border-radius: 12px;
                    box-shadow: 0 4px 12px rgba(0,0,0,0.1);
                    border: 1px solid #ddd;
                }}
                
                .header {{
                    text-align: center;
                    margin-bottom: 25px;
                }}
                
                .header h1 {{
                    color: #2c3e50;
                    margin: 0;
                    font-size: 24px;
                }}
                
                .header p {{
                    color: #7f8c8d;
                    margin: 5px 0;
                    font-size: 14px;
                }}
                
                .instructions {{
                    background: #e3f2fd;
                    border-left: 4px solid #2196F3;
                    padding: 15px;
                    margin-bottom: 20px;
                    border-radius: 4px;
                }}
                
                .instructions h4 {{
                    margin: 0 0 10px 0;
                    color: #1565c0;
                }}
                
                .instructions ol {{
                    margin: 5px 0;
                    padding-left: 20px;
                    color: #666;
                    font-size: 13px;
                }}
                
                .instructions li {{
                    margin: 5px 0;
                }}
                
                .instructions em {{
                    color: #e74c3c;
                    font-weight: 600;
                }}
                
                .groups-grid {{
                    display: grid;
                    grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
                    gap: 10px;
                    padding: 15px;
                    background: #f8f9fa;
                    border-radius: 10px;
                    border: 2px solid #e0e0e0;
                    margin-bottom: 20px;
                }}
                
                .functional-group {{
                    background: white;
                    border: 2px solid #ddd;
                    border-radius: 8px;
                    padding: 12px;
                    text-align: center;
                    cursor: pointer;
                    transition: all 0.2s;
                    font-size: 14px;
                    font-weight: 600;
                    position: relative;
                    user-select: none;
                }}
                
                .functional-group:hover {{
                    border-color: #2196F3;
                    transform: translateY(-2px);
                    box-shadow: 0 2px 8px rgba(33, 150, 243, 0.2);
                }}
                
                .functional-group.selected {{
                    background-color: #fff9c4;
                    border-color: #ffc107;
                    box-shadow: 0 0 0 4px rgba(255, 193, 7, 0.3);
                    transform: scale(1.05);
                    z-index: 10;
                }}
                
                .drop-zones {{
                    display: grid;
                    grid-template-columns: 1fr 1fr;
                    gap: 20px;
                    margin-bottom: 20px;
                }}
                
                .drop-zone {{
                    border: 3px dashed #ccc;
                    border-radius: 10px;
                    min-height: 250px;
                    padding: 15px;
                    background: white;
                    transition: all 0.3s;
                    cursor: pointer;
                    position: relative;
                }}
                
                .drop-zone:hover {{
                    border-color: #999;
                    background: #fafafa;
                }}
                
                .drop-zone.ready-to-receive {{
                    border-color: #4CAF50;
                    background: #e8f5e9;
                    border-style: solid;
                    animation: pulse 1s infinite;
                }}
                
                @keyframes pulse {{
                    0%, 100% {{ transform: scale(1); }}
                    50% {{ transform: scale(1.01); }}
                }}
                
                .drop-zone-header {{
                    text-align: center;
                    padding: 10px;
                    color: white;
                    border-radius: 6px;
                    margin-bottom: 15px;
                }}
                
                .hbd-header {{
                    background: #2196F3;
                }}
                
                .hba-header {{
                    background: #9C27B0;
                }}
                
                .dropped-item {{
                    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
                    color: white;
                    padding: 8px 12px;
                    margin: 5px 0;
                    border-radius: 6px;
                    font-size: 13px;
                    cursor: pointer;
                    display: flex;
                    justify-content: space-between;
                    align-items: center;
                    transition: all 0.2s;
                }}
                
                .dropped-item:hover {{
                    opacity: 0.9;
                    transform: translateX(-2px);
                }}
                
                .button-row {{
                    display: flex;
                    gap: 10px;
                    margin-top: 15px;
                }}
                
                .button-row button {{
                    flex: 1;
                }}
                
                .btn {{
                    padding: 10px 20px;
                    border: none;
                    border-radius: 6px;
                    font-weight: 600;
                    cursor: pointer;
                    transition: all 0.2s;
                    font-size: 14px;
                }}
                
                .btn-check {{
                    background: #2196F3;
                    color: white;
                }}
                
                .btn-check:hover {{
                    background: #1976D2;
                }}
                
                .btn-reset {{
                    background: #e74c3c;
                    color: white;
                }}
                
                .btn-reset:hover {{
                    background: #c0392b;
                }}
                
                .feedback {{
                    margin-top: 20px;
                    padding: 15px;
                    border-radius: 8px;
                    background: #f8f9fa;
                    display: none;
                    border: 1px solid #ddd;
                }}
                
                .feedback.show {{
                    display: block;
                }}
                
                .feedback h4 {{
                    margin-top: 0;
                    color: #2c3e50;
                }}
                
                .result-box {{
                    margin: 10px 0;
                }}
                
                .correct-mark {{
                    color: #27ae60;
                    font-weight: bold;
                }}
                
                .incorrect-mark {{
                    color: #e74c3c;
                    font-weight: bold;
                }}
                
                .info-box {{
                    margin-top: 20px;
                    font-size: 12px;
                    color: #666;
                    background: #f0f0f0;
                    padding: 12px;
                    border-radius: 6px;
                }}
                
                .info-box strong {{
                    color: #2c3e50;
                }}
            </style>
        </head>
        <body>
            <div class="quiz-container">
                <div class="header">
                    <h1>Functional Groups: H-Bond Donors & Acceptors</h1>
                    <p>Identify which functional groups can donate or accept hydrogen bonds</p>
                </div>

                <div class="instructions">
                    <h4>📋 Instructions:</h4>
                    <ol>
                        <li><strong>Click a functional group</strong> to select it (it will turn yellow)</li>
                        <li><strong>Click a box</strong> (H-Bond Donors or H-Bond Acceptors) to place it there</li>
                        <li><em>Important: Some groups belong in BOTH boxes!</em></li>
                        <li>Click on placed items to remove them</li>
                        <li>Press "Check Answer" when done</li>
                    </ol>
                </div>
                
                <div class="groups-grid" id="sourceGrid">
                    {self._generate_groups_html()}
                </div>
                
                <div class="drop-zones">
                    <div class="drop-zone" id="hbdZone" data-zone-type="hbd">
                        <div class="drop-zone-header hbd-header">
                            <strong>H-Bond DONORS</strong><br>
                            <small>(H on N/O/F)</small>
                        </div>
                        <div id="hbdItems"></div>
                    </div>
                    
                    <div class="drop-zone" id="hbaZone" data-zone-type="hba">
                        <div class="drop-zone-header hba-header">
                            <strong>H-Bond ACCEPTORS</strong><br>
                            <small>(Lone pairs on N/O/F)</small>
                        </div>
                        <div id="hbaItems"></div>
                    </div>
                </div>
                
                <div class="button-row">
                    <button class="btn btn-check" id="checkBtn">✓ Check Answer</button>
                    <button class="btn btn-reset" id="resetBtn">🔄 Reset</button>
                </div>
                
                <div class="feedback" id="feedback"></div>
                
                <div class="info-box">
                    <strong>ℹ️ About Hydrogen Bonding:</strong> Hydrogen bonds are crucial for drug-receptor interactions. 
                    <strong>Donors</strong> have H attached to N, O, or F. <strong>Acceptors</strong> have lone pairs on N, O, or F. 
                    Many functional groups can do both (e.g., -OH, -NH₂).
                </div>
            </div>
            
            <script>
                (function() {{
                    // --- STATE MANAGEMENT ---
                    const correctAnswers = {correct_answers_json};
                    let currentlySelectedName = null;
                    let currentlySelectedElement = null;

                    // --- SELECTION LOGIC ---
                    function selectGroup(name, element) {{
                        // Remove highlighting from previous selection
                        if (currentlySelectedElement) {{
                            currentlySelectedElement.classList.remove('selected');
                        }}
                        
                        // If clicking the same one, deselect it
                        if (currentlySelectedName === name) {{
                            currentlySelectedName = null;
                            currentlySelectedElement = null;
                            updateZoneVisuals(false);
                            return;
                        }}
                        
                        // Select new one
                        currentlySelectedName = name;
                        currentlySelectedElement = element;
                        element.classList.add('selected');
                        updateZoneVisuals(true);
                    }}

                    function updateZoneVisuals(active) {{
                        const zones = document.querySelectorAll('.drop-zone');
                        zones.forEach(z => {{
                            if (active) {{
                                z.classList.add('ready-to-receive');
                            }} else {{
                                z.classList.remove('ready-to-receive');
                            }}
                        }});
                    }}

                    // --- PLACEMENT LOGIC ---
                    function handleZoneClick(zoneType) {{
                        if (!currentlySelectedName) return;
                        addItemToZone(currentlySelectedName, zoneType);
                        
                        // Deselect after placing
                        if (currentlySelectedElement) {{
                            currentlySelectedElement.classList.remove('selected');
                        }}
                        currentlySelectedName = null;
                        currentlySelectedElement = null;
                        updateZoneVisuals(false);
                    }}

                    function addItemToZone(name, zoneType) {{
                        const containerId = zoneType === 'hbd' ? 'hbdItems' : 'hbaItems';
                        const container = document.getElementById(containerId);
                        
                        // Check for duplicates
                        const existingItems = Array.from(container.children);
                        if (existingItems.some(el => el.dataset.name === name)) {{
                            return;
                        }}

                        const div = document.createElement('div');
                        div.className = 'dropped-item';
                        div.dataset.name = name;
                        div.innerHTML = '<span>' + name + '</span> <span>✕</span>';
                        div.onclick = function(e) {{
                            e.stopPropagation();
                            div.remove();
                        }};
                        
                        container.appendChild(div);
                    }}

                    // --- SETUP CLICK HANDLERS ---
                    function setupClickHandlers() {{
                        // Functional group clicks
                        const groups = document.querySelectorAll('.functional-group');
                        groups.forEach(group => {{
                            group.addEventListener('click', function(e) {{
                                e.stopPropagation();
                                const name = this.dataset.name;
                                selectGroup(name, this);
                            }});
                        }});

                        // Zone clicks
                        const zones = document.querySelectorAll('.drop-zone');
                        zones.forEach(zone => {{
                            zone.addEventListener('click', function(e) {{
                                // Only handle if clicking the zone itself, not a dropped item
                                if (e.target.classList.contains('drop-zone') || 
                                    e.target.classList.contains('drop-zone-header') ||
                                    e.target.closest('.drop-zone-header')) {{
                                    const zoneType = this.dataset.zoneType;
                                    handleZoneClick(zoneType);
                                }}
                            }});
                        }});

                        // Check button
                        document.getElementById('checkBtn').addEventListener('click', checkAnswer);
                        
                        // Reset button
                        document.getElementById('resetBtn').addEventListener('click', resetQuiz);
                    }}

                    // --- CHECKING LOGIC ---
                    function checkAnswer() {{
                        const hbdItems = Array.from(document.getElementById('hbdItems').children).map(el => el.dataset.name);
                        const hbaItems = Array.from(document.getElementById('hbaItems').children).map(el => el.dataset.name);
                        
                        let html = '<h4>Results</h4>';
                        
                        function checkZone(userList, propName, label) {{
                            let correctList = Object.keys(correctAnswers).filter(k => correctAnswers[k][propName]);
                            let zoneHtml = '<div class="result-box"><strong>' + label + ':</strong><br>';
                            
                            if (userList.length === 0) {{
                                zoneHtml += '<span style="color:#999; font-style:italic;">Empty - add some groups!</span><br>';
                            }}

                            userList.forEach(item => {{
                                if (correctAnswers[item] && correctAnswers[item][propName]) {{
                                    zoneHtml += '<span class="correct-mark">✓ ' + item + '</span><br>';
                                }} else {{
                                    zoneHtml += '<span class="incorrect-mark">✗ ' + item + ' (Should not be here)</span><br>';
                                }}
                            }});
                            
                            // Check for missing
                            let missing = correctList.filter(x => !userList.includes(x));
                            if (missing.length > 0) {{
                                zoneHtml += '<span style="color:#e74c3c; font-size:0.9em;">❌ Missing: ' + missing.join(', ') + '</span>';
                            }} else if (userList.length > 0) {{
                                zoneHtml += '<span style="color:#27ae60; font-size:0.9em;">✅ Complete!</span>';
                            }}
                            
                            zoneHtml += '</div><hr>';
                            return zoneHtml;
                        }}

                        html += checkZone(hbdItems, 'hbd', 'H-Bond Donors');
                        html += checkZone(hbaItems, 'hba', 'H-Bond Acceptors');
                        
                        // Check if perfect score
                        let hbdCorrect = Object.keys(correctAnswers).filter(k => correctAnswers[k]['hbd']);
                        let hbaCorrect = Object.keys(correctAnswers).filter(k => correctAnswers[k]['hba']);
                        let hbdPerfect = hbdCorrect.every(x => hbdItems.includes(x)) && hbdItems.every(x => hbdCorrect.includes(x));
                        let hbaPerfect = hbaCorrect.every(x => hbaItems.includes(x)) && hbaItems.every(x => hbaCorrect.includes(x));
                        
                        if (hbdPerfect && hbaPerfect) {{
                            html += '<div style="background:#d4edda; color:#155724; padding:12px; border-radius:6px; text-align:center; margin-top:10px; border:1px solid #c3e6cb;"><strong>🎉 Perfect!</strong> You got them all correct!</div>';
                        }}
                        
                        const fb = document.getElementById('feedback');
                        fb.innerHTML = html;
                        fb.className = 'feedback show';
                        fb.scrollIntoView({{behavior: 'smooth', block: 'nearest'}});
                    }}

                    function resetQuiz() {{
                        document.getElementById('hbdItems').innerHTML = '';
                        document.getElementById('hbaItems').innerHTML = '';
                        document.getElementById('feedback').className = 'feedback';
                        currentlySelectedName = null;
                        if (currentlySelectedElement) {{
                            currentlySelectedElement.classList.remove('selected');
                        }}
                        currentlySelectedElement = null;
                        updateZoneVisuals(false);
                    }}

                    // --- INITIALIZE ---
                    setTimeout(function() {{
                        setupClickHandlers();
                    }}, 100);
                }})();
            </script>
        </body>
        </html>
        '''
        
        display(HTML(html_code))
    
    def _generate_groups_html(self):
        html = ''
        for fg in self.functional_groups:
            name = fg['name']
            html += f'<div class="functional-group" data-name="{name}">{name}</div>'
        return html

# Run the quiz
quiz = FunctionalGroupInteractive()
quiz.create_interface()

Functional Groups: H-Bond Donors & Acceptors

Identify which functional groups can donate or accept hydrogen bonds

📋 Instructions:

  1. Click a functional group to select it (it will turn yellow)
  2. Click a box (H-Bond Donors or H-Bond Acceptors) to place it there
  3. Important: Some groups belong in BOTH boxes!
  4. Click on placed items to remove them
  5. Press "Check Answer" when done
Alcohol (-OH)
Primary Amine (-NH₂)
Carbonyl (C=O)
Ether (-O-)
Carboxylic Acid (-COOH)
Amide (-CONH-)
Tertiary Amine (R₃N)
Ester (-COO-)
Phenol (Ar-OH)
Pyridine N
Nitrile (-C≡N)
Methyl (CH₃-)
H-Bond DONORS
(H on N/O/F)
H-Bond ACCEPTORS
(Lone pairs on N/O/F)
ℹ️ About Hydrogen Bonding: Hydrogen bonds are crucial for drug-receptor interactions. Donors have H attached to N, O, or F. Acceptors have lone pairs on N, O, or F. Many functional groups can do both (e.g., -OH, -NH₂).

Hide code cell source

from rdkit import Chem
from rdkit.Chem import AllChem, Descriptors
import numpy as np
import json
from IPython.display import HTML

def identify_hba_hbd(mol):
    """Identify hydrogen bond acceptor and donor atoms"""
    hba_atoms = []  # Electronegative atoms (O, N) that can accept H-bonds
    hbd_atoms = []  # H atoms bonded to O/N that can donate H-bonds
    
    for atom in mol.GetAtoms():
        idx = atom.GetIdx()
        symbol = atom.GetSymbol()
        
        # HBA: Oxygen or Nitrogen atoms with lone pairs
        if symbol in ['O', 'N']:
            # Skip quaternary nitrogen (N+)
            if symbol == 'N' and atom.GetFormalCharge() > 0:
                continue
            hba_atoms.append(idx)
        
        # HBD: Hydrogen bonded to O or N
        if symbol == 'H':
            for neighbor in atom.GetNeighbors():
                if neighbor.GetSymbol() in ['O', 'N']:
                    hbd_atoms.append(idx)
                    break
    
    return hba_atoms, hbd_atoms

def create_hba_hbd_game():
    """Create interactive game for identifying HBA and HBD atoms"""
    
    # Define drug molecules
    molecules = {
        'level1': {
            'name': 'Aspirin',
            'smiles': 'CC(=O)Oc1ccccc1C(=O)O',
            'description': 'Pain reliever - Simple structure',
            'difficulty': 'Beginner'
        },
        'level2': {
            'name': 'Atorvastatin',
            'smiles': 'CC(C)c1c(C(=O)Nc2ccccc2)c(-c2ccccc2)c(-c2ccc(F)cc2)n1CC[C@@H](O)C[C@@H](O)CC(=O)O',
            'description': 'Cholesterol drug - Multiple H-bond sites',
            'difficulty': 'Intermediate'
        },
        'level3': {
            'name': 'Imatinib',
            'smiles': 'Cc1ccc(NC(=O)c2ccc(CN3CCN(C)CC3)cc2)cc1Nc1nccc(-c2cccnc2)n1',
            'description': 'Cancer drug - Complex H-bonding network',
            'difficulty': 'Advanced'
        }
    }
    
    # Generate 3D structures and identify H-bond sites
    mol_data = {}
    for level, data in molecules.items():
        mol = Chem.MolFromSmiles(data['smiles'])
        mol = Chem.AddHs(mol)
        
        # Robust embedding
        res = AllChem.EmbedMolecule(mol, randomSeed=42)
        if res == -1:
            AllChem.EmbedMolecule(mol, useRandomCoords=True)
            
        AllChem.MMFFOptimizeMolecule(mol)
        
        hba_atoms, hbd_atoms = identify_hba_hbd(mol)
        xyz_block = Chem.MolToXYZBlock(mol)
        
        mol_data[level] = {
            'xyz': xyz_block,
            'hba_atoms': hba_atoms,
            'hbd_atoms': hbd_atoms,
            'num_hba': len(hba_atoms),
            'num_hbd': len(hbd_atoms),
            'name': data['name'],
            'description': data['description'],
            'difficulty': data['difficulty']
        }
    
    mol_data_json = json.dumps(mol_data)
    viewer_id = f"hbond_viewer_{np.random.randint(100000, 999999)}"
    
    html = f"""
    <!DOCTYPE html>
    <html>
    <head>
        <script src="https://3Dmol.csb.pitt.edu/build/3Dmol-min.js"></script>
        <style>
            .game-container {{
                font-family: 'Segoe UI', Arial, sans-serif;
                max-width: 900px; margin: 0 auto; padding: 20px;
                background: #fff; border-radius: 12px;
                box-shadow: 0 4px 12px rgba(0,0,0,0.1); border: 1px solid #ddd;
            }}
            .header {{ text-align: center; margin-bottom: 25px; }}
            .header h1 {{ color: #2c3e50; margin: 0; }}
            .level-selector {{ display: flex; gap: 10px; margin-bottom: 20px; }}
            .level-btn {{
                flex: 1; padding: 12px; border: 2px solid #eee;
                border-radius: 8px; cursor: pointer; transition: all 0.2s;
                text-align: center; background: #f8f9fa;
            }}
            .level-btn:hover {{ border-color: #3498db; background: #ebf5fb; }}
            .level-btn.active {{ border-color: #3498db; background: #e3f2fd; color: #2980b9; }}
            
            .mode-toggle {{
                display: flex; gap: 10px; margin-bottom: 20px; justify-content: center;
            }}
            .mode-btn {{
                padding: 10px 24px; border: 2px solid #eee;
                border-radius: 8px; cursor: pointer; transition: all 0.2s;
                background: #f8f9fa; font-weight: 600;
            }}
            .mode-btn:hover {{ border-color: #3498db; }}
            .mode-btn.active {{ border-color: #3498db; background: #e3f2fd; color: #2980b9; }}
            
            .viewer-wrapper {{
                height: 450px; width: 100%; position: relative;
                border: 1px solid #eee; border-radius: 8px; overflow: hidden;
            }}
            
            .status-bar {{
                display: flex; justify-content: space-around;
                padding: 15px; background: #f8f9fa; border-radius: 8px; margin-bottom: 15px;
            }}
            .stat-box {{ text-align: center; }}
            .stat-val {{ font-size: 20px; font-weight: bold; color: #2c3e50; }}
            .stat-label {{ font-size: 12px; color: #7f8c8d; text-transform: uppercase; }}
            
            .completion-msg {{
                display: none; background: #d4edda; color: #155724;
                padding: 15px; border-radius: 8px; text-align: center; margin-top: 15px;
                border: 1px solid #c3e6cb;
            }}
            
            .btn-reset {{
                display: block; width: 100%; padding: 12px; margin-top: 15px;
                background: #3498db; color: white; border: none; border-radius: 6px;
                cursor: pointer; font-weight: 600;
            }}
            .btn-reset:hover {{ background: #2980b9; }}
        </style>
    </head>
    <body>
        <div class="game-container">

            <div style="background: #fff3e0; border-left: 4px solid #ff9800; padding: 15px; margin-bottom: 20px; border-radius: 4px;">
                <h4 style="margin: 0 0 10px 0; color: #e65100;">📋 Instructions:</h4>
                <ol style="margin: 5px 0; padding-left: 20px; color: #666;">
                    <li><strong>Select mode:</strong> HBA (H-Bond Acceptors) or HBD (H-Bond Donors)</li>
                    <li><strong>HBA:</strong> Click on O or N atoms that can accept H-bonds</li>
                    <li><strong>HBD:</strong> Click on H atoms bonded to O/N that can donate H-bonds</li>
                </ol>
            </div>
            
            <div class="level-selector">
                <div class="level-btn active" onclick="window.game_{viewer_id}.setLevel('level1', this)">
                    <strong>Level 1</strong><br><span style="font-size:12px">Aspirin</span>
                </div>
                <div class="level-btn" onclick="window.game_{viewer_id}.setLevel('level2', this)">
                    <strong>Level 2</strong><br><span style="font-size:12px">Atorvastatin</span>
                </div>
                <div class="level-btn" onclick="window.game_{viewer_id}.setLevel('level3', this)">
                    <strong>Level 3</strong><br><span style="font-size:12px">Imatinib</span>
                </div>
            </div>

            <div class="mode-toggle">
                <button class="mode-btn active" onclick="window.game_{viewer_id}.setMode('hba', this)">
                    HBA Mode (O, N atoms)
                </button>
                <button class="mode-btn" onclick="window.game_{viewer_id}.setMode('hbd', this)">
                    HBD Mode (H atoms)
                </button>
            </div>

            <div class="status-bar">
                <div class="stat-box">
                    <div class="stat-label">Mode</div>
                    <div class="stat-val" style="color:#3498db" id="mode_{viewer_id}">HBA</div>
                </div>
                <div class="stat-box">
                    <div class="stat-label">Total</div>
                    <div class="stat-val" id="total_{viewer_id}">0</div>
                </div>
                <div class="stat-box">
                    <div class="stat-label">Found</div>
                    <div class="stat-val" style="color:#27ae60" id="found_{viewer_id}">0</div>
                </div>
                <div class="stat-box">
                    <div class="stat-label">Remaining</div>
                    <div class="stat-val" style="color:#e74c3c" id="rem_{viewer_id}">0</div>
                </div>
            </div>

            <div id="viewer_{viewer_id}" class="viewer-wrapper"></div>
            
            <div id="msg_{viewer_id}" class="completion-msg">
                <strong>🎉 Success!</strong> You identified all H-bond sites in this mode.
            </div>
            
            <button class="btn-reset" onclick="window.game_{viewer_id}.reset()">🔄 Reset Level</button>
            
            <div style="margin-top:20px; font-size: 13px; color: #666; background: #fff3cd; padding: 10px; border-radius: 6px;">
                <strong>💡 Tip:</strong> Blue = HBA (acceptor), Orange = HBD (donor). 
                Green flash = Correct, Red flash = Incorrect.
            </div>
        </div>

        <script>
        window.game_{viewer_id} = (function() {{
            const molecules = {mol_data_json};
            let viewer = null;
            let currentLevel = 'level1';
            let currentMode = 'hba'; // 'hba' or 'hbd'
            let foundHBA = new Set();
            let foundHBD = new Set();
            let clicked = new Set();
            
            function init() {{
                viewer = $3Dmol.createViewer("viewer_{viewer_id}", {{
                    backgroundColor: 'white'
                }});
                loadLevel('level1');
            }}
            
            function loadLevel(level) {{
                currentLevel = level;
                foundHBA.clear();
                foundHBD.clear();
                clicked.clear();
                document.getElementById('msg_{viewer_id}').style.display = 'none';
                
                let data = molecules[level];
                viewer.clear();
                viewer.addModel(data.xyz, "xyz");
                
                viewer.setStyle({{}}, {{stick: {{radius: 0.15}}, sphere: {{scale: 0.3}}}});
                
                viewer.setClickable({{}}, true, function(atom) {{
                    handleAtomClick(atom);
                }});
                
                viewer.zoomTo();
                viewer.render();
                updateStats();
            }}
            
            function handleAtomClick(atom) {{
                if(clicked.has(atom.index)) return;
                
                let data = molecules[currentLevel];
                let isCorrect = false;
                let targetSet = null;
                
                if(currentMode === 'hba') {{
                    isCorrect = data.hba_atoms.includes(atom.index);
                    targetSet = foundHBA;
                }} else {{
                    isCorrect = data.hbd_atoms.includes(atom.index);
                    targetSet = foundHBD;
                }}
                
                if(isCorrect) {{
                    if(!targetSet.has(atom.index)) {{
                        targetSet.add(atom.index);
                        flashAtom(atom, '#2ecc71');
                        updateStats();
                        checkWin();
                    }}
                }} else {{
                    flashAtom(atom, '#e74c3c');
                }}
            }}
            
            function flashAtom(atom, color) {{
                viewer.setStyle({{index: atom.index}}, {{
                    stick: {{radius: 0.15}},
                    sphere: {{color: color, scale: 0.6}}
                }});
                viewer.render();
                
                clicked.add(atom.index);
                setTimeout(() => {{
                    let finalColor = null;
                    let finalScale = 0.3;
                    
                    if(foundHBA.has(atom.index)) {{
                        finalColor = '#3498db'; // Blue for HBA
                        finalScale = 0.45;
                    }} else if(foundHBD.has(atom.index)) {{
                        finalColor = '#ff9800'; // Orange for HBD
                        finalScale = 0.45;
                    }}
                    
                    let style = {{ stick: {{radius: 0.15}} }};
                    if(finalColor) {{
                        style.sphere = {{color: finalColor, scale: finalScale}};
                    }} else {{
                        style.sphere = {{scale: 0.3}};
                    }}
                    
                    viewer.setStyle({{index: atom.index}}, style);
                    viewer.render();
                    clicked.delete(atom.index);
                }}, 600);
            }}
            
            function updateStats() {{
                let data = molecules[currentLevel];
                let total, found;
                
                if(currentMode === 'hba') {{
                    total = data.num_hba;
                    found = foundHBA.size;
                }} else {{
                    total = data.num_hbd;
                    found = foundHBD.size;
                }}
                
                document.getElementById('mode_{viewer_id}').innerText = currentMode.toUpperCase();
                document.getElementById('total_{viewer_id}').innerText = total;
                document.getElementById('found_{viewer_id}').innerText = found;
                document.getElementById('rem_{viewer_id}').innerText = total - found;
            }}
            
            function checkWin() {{
                let data = molecules[currentLevel];
                let complete = false;
                
                if(currentMode === 'hba') {{
                    complete = foundHBA.size === data.num_hba;
                }} else {{
                    complete = foundHBD.size === data.num_hbd;
                }}
                
                if(complete) {{
                    document.getElementById('msg_{viewer_id}').style.display = 'block';
                }}
            }}
            
            setTimeout(init, 100);
            
            return {{
                setLevel: function(level, btnElement) {{
                    let btns = document.querySelectorAll('.level-btn');
                    btns.forEach(b => b.classList.remove('active'));
                    if(btnElement) btnElement.classList.add('active');
                    loadLevel(level);
                }},
                setMode: function(mode, btnElement) {{
                    currentMode = mode;
                    document.getElementById('msg_{viewer_id}').style.display = 'none';
                    
                    let btns = document.querySelectorAll('.mode-btn');
                    btns.forEach(b => b.classList.remove('active'));
                    if(btnElement) btnElement.classList.add('active');
                    
                    updateStats();
                }},
                reset: function() {{
                    loadLevel(currentLevel);
                }}
            }};
        }})();
        </script>
    </body>
    </html>
    """
    
    return HTML(html)

create_hba_hbd_game()

📋 Instructions:

  1. Select mode: HBA (H-Bond Acceptors) or HBD (H-Bond Donors)
  2. HBA: Click on O or N atoms that can accept H-bonds
  3. HBD: Click on H atoms bonded to O/N that can donate H-bonds
Level 1
Aspirin
Level 2
Atorvastatin
Level 3
Imatinib
Mode
HBA
Total
0
Found
0
Remaining
0
🎉 Success! You identified all H-bond sites in this mode.
💡 Tip: Blue = HBA (acceptor), Orange = HBD (donor). Green flash = Correct, Red flash = Incorrect.

3.2 How Ionization State Affects Hydrogen Bonding#

The pKa-dependent ionization state dramatically alters H-bonding capability, with direct consequences for drug behavior:

For example, for acidic group (e.g., carboxylic acids, phenols):

State

pH Condition

Form

HBD

HBA

Implication

Neutral

pH < pKa

COOH

1

2

Can both donate and accept; more lipophilic

Ionized

pH > pKa

COO⁻

0

2

Only accepts; highly hydrophilic; charged

Example - Aspirin (pKa 3.5):

Stomach (pH 2): Predominantly COOH form → retains 1 HBD, membrane permeable

Blood (pH 7.4): Predominantly COO⁻ form → loses HBD, becomes charged and water-soluble

For basic groups (e.g., amines):

State

pH Condition

Form

HBD

HBA

Implication

Neutral

pH > pKa

NH₂ or NH

1-2

1

Can donate and accept; membrane permeable

Ionized

pH < pKa

NH₃⁺ or NH₂⁺

2-3

0

Only donates; charged; loses acceptor capability

Example - Morphine (pKa 8.0):

Intestine (pH 6.5): Mostly NH₃⁺ form → gains HBD capacity, loses HBA, charged Blood (pH 7.4): Mix of both forms → complex binding profile


4. Summary and Key Takeaways#

In this section, we’ve explored the fundamental concepts of hydrogen bond donors and acceptors and their critical role in hydrogen bond formation.

  • H-bonds mediate molecular recognition: Donors (NH, OH) and acceptors (C=O, N, O) form the interaction points for target binding, with typical binding contributions of 0.5-1.5 kcal/mol per bond—multiple H-bonds are essential for nanomolar affinity.

  • Ionization alters H-bonding capacity: Protonation/deprotonation changes donor/acceptor counts—neutral carboxylic acids (COOH) donate and accept, while carboxylates (COO⁻) only accept; neutral amines (NH₂) both donate and accept, while ammonium ions (NH₃⁺) only donate but with greater strength.

Understanding these simple counts is a foundational step in computational drug design.