Komal A Chavhan

Erstelle dein Portfolio mit ProoVErstelle dein Portfolio mit ProoV
ProoV
EN Verifiziertes Arbeitsportfolio

ProoV Portfolio

Komal A Chavhan

Computer Science · SJB Institute of Technology

Zum ProoV-Leaderboard

Projekte

Zertifiziert

Selbstgesteuertes Projekt

Frontend Engineering with Prediction Game

Junior Front-End Engineer · Softwareentwicklung · Juli 2026

92/ 100

Built a complete World Cup prediction game end to end: rendered the fixture board with score inputs, implemented exact/result-only scoring, computed team strengths from real match data, and used those strengths in a Poisson-style house bot. Also shipped a persistent ranked leaderboard that survives reloads and deployed the finished game to a live https URL.

Bewertet nach

  • Scoring engine correctness30%
  • Prediction model genuinely from real data25%
  • Game completeness and leaderboard soundness25%
  • Shipped and shareable: live deployed URL20%

Bestanden · Bestehensgrenze 60/100

Was herausstach6
  • Implemented the exact scoring rule, including the draw-vs-draw edge case returning 2
  • Computed team attack/defense strengths from the embedded real-results sample instead of hard-coding a stub
  • Built a bot predictor that uses both teams' strengths through a Poisson-style lambda calculation
  • Persisted and reloaded leaderboard rows under one localStorage key, then sorted them descending
  • Leakage-Free Feature Engineering
  • Honest Model Evaluation
Meine eingereichte Arbeit9 Aufgaben

Lab Orientation

Meine Lösung
{"code":"<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <title>Predict the Cup</title>\n    <style>\n      body {\n        font-family: -apple-system, sans-serif;\n        background: #FDFCF7;\n        color: #0E1726;\n        padding: 32px;\n        margin: 0;\n      }\n      h1 {\n        color: #00C65A;\n        font-size: 32px;\n        margin: 0 0 12px 0;\n      }\n      p { color: #3F4B5F; margin: 0; }\n    </style>\n  </head>\n  <body>\n    <!-- TODO: change this title to something of your own -->\n    <h1>Hello There!!</h1>\n    <p>How is the prediction game going on?</p>\n  </body>\n</html>","ranAt":"2026-07-22T02:43:50.815Z"}

Act1 Fixtures Form

Meine Lösung
{"code":"<!doctype html>\n<html><head>\n  <meta charset=\"utf-8\" />\n  <title>Predict the Cup · my picks</title>\n  <style>\n    body { font: 15px/1.45 system-ui, -apple-system, \"Segoe UI\", sans-serif;\n           padding: 24px; background: #F7F8FA; color: #0E1726; }\n    h1   { margin: 0 0 4px; font-size: 22px; }\n    .sub { color: #6A7589; font-size: 13px; margin-bottom: 16px; }\n    #board { display: grid; gap: 12px; max-width: 640px; }\n    .match { background: #fff; border: 1px solid #E4E8EE; border-radius: 12px;\n             padding: 14px; }\n    .meta  { color: #6A7589; font-size: 11px; text-transform: uppercase;\n             letter-spacing: 0.14em; margin-bottom: 6px; }\n    .teams { font-weight: 800; font-size: 15.5px; margin-bottom: 8px; }\n    .picks { display: grid; grid-template-columns: 1fr auto 1fr; gap: 8px; align-items: center; }\n    .picks input { padding: 9px; border: 1px solid #E4E8EE; border-radius: 8px;\n                   text-align: center; font: inherit; font-weight: 700; font-size: 15px; }\n    .vs    { color: #6A7589; font-weight: 700; font-size: 12px; }\n    footer { color: #6A7589; font-size: 11px; margin-top: 24px; max-width: 640px; line-height: 1.5; }\n  </style>\n</head><body>\n\n  <h1>My picks</h1>\n  <div class=\"sub\">2026 World Cup · Round of 32</div>\n  <div id=\"board\"></div>\n\n  <footer>\n    Independent learning project. Not affiliated with or endorsed by FIFA.\n    Fixtures snapshot: openfootball/worldcup.json (CC0-1.0).\n  </footer>\n\n  <script>\n    /* The real 2026 fixtures (sample slice of the bundled snapshot). */\n    const FIXTURES = [\n      { home: \"Germany\",   away: \"Mexico\",      date: \"2026-06-28\", round: \"R32\" },\n      { home: \"Argentina\", away: \"Switzerland\", date: \"2026-06-29\", round: \"R32\" },\n      { home: \"France\",    away: \"Portugal\",    date: \"2026-06-30\", round: \"R32\" },\n      { home: \"Spain\",     away: \"Uruguay\",     date: \"2026-07-01\", round: \"R32\" },\n      { home: \"Brazil\",    away: \"Japan\",       date: \"2026-07-02\", round: \"R32\" },\n      { home: \"England\",   away: \"Netherlands\", date: \"2026-07-03\", round: \"R32\" },\n    ];\n\n    /* TODO — Your task.\n     * Loop FIXTURES and, for each match, build a card and append it to #board.\n     * Each card MUST:\n     *   - be tagged with the attribute  data-match  (any value is fine)\n     *   - contain TWO  <input type=\"number\">  (one for each team's predicted score)\n     *   - show both team names somewhere visible\n     *\n     * Tip: the worked example is the same shape on a 2-match sample.\n     */\n    function renderFixtures() {\n      const board = document.getElementById(\"board\");\n      FIXTURES.forEach((fixture, index) => {\n        const card = document.createElement(\"div\");\n        card.className = \"match\";\n        card.setAttribute(\"data-match\", index);\n        card.innerHTML = `\n          <div class=\"meta\">${fixture.round} vs ${fixture.date}</div>\n          <div class=\"teams\">${fixture.home} vs ${fixture.away}</div>\n          <div class=\"picks\">\n            <input type=\"number\" min=\"0\">\n            <span class=\"vs\">VS</span>\n            <input type=\"number\" min=\"0\">\n          </div>\n        `;\n\n        board.appendChild(card);\n      });\n    }\n\n    renderFixtures();\n  </script>\n\n</body></html>","ranAt":"2026-07-22T03:19:33.850Z","selfCheck":{"cases":[{"name":"FIXTURES const is in scope","pass":true,"detail":""},{"name":"One card per fixture (data-match)","pass":true,"detail":""},{"name":"Two number inputs per card","pass":true,"detail":""},{"name":"Team names visible on each card","pass":true,"detail":""}],"ranAt":"2026-07-22T03:19:33.850Z"},"passed":true,"attempt":1}

Act2 Scoring Function

Meine Lösung
{"code":"<!doctype html>\n<html lang=\"en\">\n  <head><meta charset=\"utf-8\"><title>scorePick · your turn</title></head>\n  <body>\n    <h1>scorePick · your turn</h1>\n    <pre id=\"out\" style=\"font-family:ui-monospace,Menlo,monospace;font-size:13px;white-space:pre-wrap\"></pre>\n\n    <script>\n      // ─────────────────────────────────────────────────────────────────────\n      // YOUR TASK: write the body of scorePick.\n      //\n      // It takes two scorelines and returns the points by the rule:\n      //   exact score              -> 5\n      //   right result, wrong score -> 2\n      //   wrong result             -> 0\n      //\n      // (A \"draw vs draw with different numbers\" still counts as the same\n      //  result, so 1-1 vs 2-2 should return 2, not 0.)\n      // ─────────────────────────────────────────────────────────────────────\n      function scorePick(predicted, actual) {\n        if (\n          predicted.home === actual.home &&\n          predicted.away === actual.away\n        ) {\n          return 5;\n        }\n        let predictedResult;\n        if (predicted.home > predicted.away) {\n            predictedResult = \"home\";\n          } else if (predicted.home < predicted.away) {\n            predictedResult = \"away\";\n          } else {\n            predictedResult = \"draw\";\n          }\n\n        let actualResult;\n        if (actual.home > actual.away) {\n          actualResult = \"home\";\n        } else if (actual.home < actual.away) {\n          actualResult = \"away\";\n        } else {\n          actualResult = \"draw\";\n        }\n        if (predictedResult === actualResult) {\n          return 2;\n        }\n        return 0;\n      }\n\n      // Local sanity-check, scored on screen so you can iterate without the\n      // self-check button. The grader (Self-check) runs the same four cases.\n      const cases = [\n        { pred: { home: 2, away: 1 }, actual: { home: 2, away: 1 }, why: 'exact 2-1' },\n        { pred: { home: 3, away: 0 }, actual: { home: 1, away: 0 }, why: 'home win, wrong score' },\n        { pred: { home: 0, away: 1 }, actual: { home: 2, away: 0 }, why: 'wrong result' },\n        { pred: { home: 1, away: 1 }, actual: { home: 2, away: 2 }, why: 'both draws' },\n      ];\n\n      const out = document.getElementById('out');\n      out.textContent = cases\n        .map(c => `${c.pred.home}-${c.pred.away} vs ${c.actual.home}-${c.actual.away}  -> ${scorePick(c.pred, c.actual)}  // ${c.why}`)\n        .join('\\n');\n    </script>\n  </body>\n</html>","ranAt":"2026-07-22T11:02:54.343Z","passed":true,"attempt":1,"selfCheck":{"cases":[{"name":"exact 2-1 vs 2-1 returns 5","pass":true},{"name":"home win 3-0 vs 1-0 returns 2","pass":true},{"name":"wrong result 0-1 vs 2-0 returns 0","pass":true},{"name":"draw 1-1 vs 2-2 returns 2","pass":true}],"ranAt":"2026-07-22T11:02:54.343Z"}}

Act3 Compute Strength

Meine Lösung
{"passed":true,"attempt":1,"selfCheck":{"allPass":true,"failedNames":[]},"ranAt":"2026-07-22T11:17:25.163Z"}

Act3 House Bot

Meine Lösung
{"passed":true,"attempt":1,"selfCheck":{"allPass":true,"failedNames":[]},"ranAt":"2026-07-22T11:22:54.830Z"}

Example Save Rank Readonly

Meine Lösung
{"code":"<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <title>Worked example: save + rank one entry</title>\n    <style>\n      body { font-family: ui-sans-serif, system-ui, sans-serif; padding: 20px; max-width: 480px; margin: 0 auto; }\n      h1 { font-size: 18px; margin: 0 0 4px 0; }\n      .sub { color: #6B7280; font-size: 12px; margin: 0 0 14px 0; }\n      table { width: 100%; border-collapse: collapse; }\n      th, td { text-align: left; padding: 8px 10px; border-bottom: 1px solid #E5E7EB; font-size: 14px; }\n      th { color: #6B7280; font-size: 11px; text-transform: uppercase; letter-spacing: .06em; }\n      .rank { width: 36px; color: #9CA3AF; font-variant-numeric: tabular-nums; }\n      .total { text-align: right; font-weight: 700; font-variant-numeric: tabular-nums; }\n    </style>\n  </head>\n  <body>\n    <h1>Pub quiz · leaderboard</h1>\n    <p class=\"sub\">A two-game demo, saved + reloaded + sorted + painted.</p>\n    <table>\n      <thead><tr><th class=\"rank\">#</th><th>Player</th><th class=\"total\">Total</th></tr></thead>\n      <tbody id=\"board\"></tbody>\n    </table>\n\n    <script>\n      // ────────────────────────────────────────────────────────────────────\n      // 1) ONE key for the whole leaderboard. One name per box, remember.\n      // ────────────────────────────────────────────────────────────────────\n      var KEY = 'demo_quiz_v1';\n\n      // ────────────────────────────────────────────────────────────────────\n      // 2) SAVE one entry. We stringify the array so the shape survives.\n      // ────────────────────────────────────────────────────────────────────\n      var entry = { name: 'Leo', total: 14 };\n\n      var existing = JSON.parse(localStorage.getItem(KEY) || '[]'); // [] on first run\n      existing.push(entry);\n      localStorage.setItem(KEY, JSON.stringify(existing));          // stringify on save\n\n      // (Demo: add one more entry so the table isn't a single row.)\n      var existing2 = JSON.parse(localStorage.getItem(KEY) || '[]');\n      if (!existing2.find(function (r) { return r.name === 'Sam'; })) {\n        existing2.push({ name: 'Sam', total: 19 });\n        localStorage.setItem(KEY, JSON.stringify(existing2));\n      }\n\n      // ────────────────────────────────────────────────────────────────────\n      // 3) READ the box back. JSON.parse so we get the array, not a string.\n      // ────────────────────────────────────────────────────────────────────\n      var raw = localStorage.getItem(KEY);\n      var rows = JSON.parse(raw || '[]');\n\n      // ────────────────────────────────────────────────────────────────────\n      // 4) SORT highest first. b minus a flips the order to descending.\n      // ────────────────────────────────────────────────────────────────────\n      rows.sort(function (a, b) { return b.total - a.total; });\n\n      // ────────────────────────────────────────────────────────────────────\n      // 5) PAINT the rows. One template string per entry, joined into HTML.\n      // ────────────────────────────────────────────────────────────────────\n      var html = rows.map(function (r, i) {\n        return ''\n          + '<tr>'\n          + '<td class=\"rank\">' + (i + 1) + '</td>'\n          + '<td>' + r.name + '</td>'\n          + '<td class=\"total\">' + r.total + '</td>'\n          + '</tr>';\n      }).join('');\n\n      document.getElementById('board').innerHTML = html;\n    </script>\n  </body>\n</html>\n","ranAt":"2026-07-22T11:24:15.659Z"}

Act4 Leaderboard

Meine Lösung
{"code":"<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <title>Section 4 · the leaderboard</title>\n    <style>\n      body { font-family: ui-sans-serif, system-ui, sans-serif; padding: 20px; max-width: 520px; margin: 0 auto; }\n      h1 { font-size: 18px; margin: 0 0 4px 0; }\n      .sub { color: #6B7280; font-size: 12px; margin: 0 0 14px 0; }\n      table { width: 100%; border-collapse: collapse; }\n      th, td { text-align: left; padding: 8px 10px; border-bottom: 1px solid #E5E7EB; font-size: 14px; }\n      th { color: #6B7280; font-size: 11px; text-transform: uppercase; letter-spacing: .06em; }\n      .rank { width: 36px; color: #9CA3AF; font-variant-numeric: tabular-nums; }\n      .total { text-align: right; font-weight: 700; font-variant-numeric: tabular-nums; }\n      button { font: inherit; padding: 8px 14px; border-radius: 999px; border: 0; background: #00C65A; color: #fff; font-weight: 700; cursor: pointer; }\n    </style>\n  </head>\n  <body>\n    <h1>Predict the Cup · leaderboard</h1>\n    <p class=\"sub\">One box, one ranked table.</p>\n    <table>\n      <thead><tr><th class=\"rank\">#</th><th>Player</th><th class=\"total\">Total</th></tr></thead>\n      <tbody id=\"board\"></tbody>\n    </table>\n    <p style=\"margin-top:14px\"><button id=\"run\">Run saveAndRank()</button></p>\n\n    <script>\n      // ────────────────────────────────────────────────────────────────────\n      // One key for the whole leaderboard. Don't change the name, the\n      // self-check reads from this exact key.\n      // ────────────────────────────────────────────────────────────────────\n      var KEY = 'proov_predict_v1';\n\n      // These three totals are what your file produces from scorePick + bot.\n      // For this slide they're hard-coded so you focus on persistence + rank.\n      var rowsThisRound = [\n        { name: 'You',     total: 17 },\n        { name: 'House bot', total: 11 },\n        { name: 'Alex',    total: 22 }\n      ];\n\n      // ────────────────────────────────────────────────────────────────────\n      // Helper: paint an array of rows into the #board tbody.\n      // (No need to touch this. Your job is to feed it the right rows.)\n      // ────────────────────────────────────────────────────────────────────\n      function render(rows) {\n        var html = rows.map(function (r, i) {\n          return ''\n            + '<tr>'\n            + '<td class=\"rank\">' + (i + 1) + '</td>'\n            + '<td>' + r.name + '</td>'\n            + '<td class=\"total\">' + r.total + '</td>'\n            + '</tr>';\n        }).join('');\n        document.getElementById('board').innerHTML = html;\n      }\n\n      // ════════════════════════════════════════════════════════════════════\n      // YOUR TURN.\n      //\n      // Write the body of saveAndRank(entries). Three moves, like the worked\n      // example:\n      //   1) SAVE entries to localStorage under KEY (stringify on the way in).\n      //   2) READ them back from KEY (parse on the way out).\n      //   3) SORT highest total first, then render(rows) to paint the table.\n      //\n      // The self-check will:\n      //   - call saveAndRank(rowsThisRound)\n      //   - check the box at KEY holds the array with all three totals\n      //   - check the rendered rows are ordered highest -> lowest\n      //   - simulate a reload (re-read KEY, re-sort) and check the order again\n      // ════════════════════════════════════════════════════════════════════\n      function saveAndRank(entries) {\n        localStorage.setItem(KEY, JSON.stringify(entries));\n        const saved = JSON.parse(localStorage.getItem(KEY));\n        saved.sort((a,b) => b.total - a.total);\n        render(saved);\n      }\n\n      // Wire the button so you can run your function from the page.\n      document.getElementById('run').addEventListener('click', function () {\n        saveAndRank(rowsThisRound);\n      });\n\n      // Make saveAndRank visible to t
…

Act5 Assemble

Meine Lösung
{"code":"<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\" />\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n<title>Predict the Cup — my World Cup prediction game</title>\n<style>\n  :root { --green:#00C65A; --ink:#0E1726; --soft:#3F4B5F; --faint:#6A7589; --line:#E4E8EE; --paper:#fff; --bg:#F4F7F6; }\n  * { box-sizing: border-box; }\n  body { font-family: ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", sans-serif;\n         margin:0; background:var(--bg); color:var(--ink); }\n  .wrap { max-width: 760px; margin: 0 auto; padding: 22px 18px 40px; }\n  header { display:flex; align-items:center; gap:12px; margin-bottom:6px; }\n  .badge { width:42px; height:42px; border-radius:12px; background:var(--green); color:#fff;\n           display:flex; align-items:center; justify-content:center; font-weight:900; font-size:18px; }\n  h1 { font-size:21px; margin:0; letter-spacing:-0.01em; }\n  .tag { color:var(--faint); font-size:13px; margin:2px 0 18px; }\n  h2 { font-size:13px; text-transform:uppercase; letter-spacing:0.12em; color:var(--faint); margin:24px 0 12px; }\n  #board { display:grid; grid-template-columns:1fr; gap:12px; }\n  @media (min-width:560px){ #board { grid-template-columns:1fr 1fr; } }\n  .match { background:var(--paper); border:1px solid var(--line); border-radius:14px; padding:14px; }\n  .meta { color:var(--faint); font-size:10.5px; text-transform:uppercase; letter-spacing:0.12em; margin-bottom:8px; }\n  .teams { display:grid; grid-template-columns:1fr auto 1fr; align-items:center; gap:8px; font-weight:800; font-size:15px; }\n  .teams .vs { color:var(--faint); font-weight:700; font-size:11px; }\n  .teams .away { text-align:right; }\n  .picks { display:grid; grid-template-columns:1fr auto 1fr; align-items:center; gap:8px; margin-top:10px; }\n  .picks input { padding:10px; border:1px solid var(--line); border-radius:9px; text-align:center;\n                 font:inherit; font-weight:800; font-size:16px; width:100%; }\n  .picks input:focus { outline:2px solid var(--green); border-color:var(--green); }\n  .picks .dash { color:var(--faint); font-weight:800; text-align:center; }\n  .botline { margin-top:10px; font-size:12px; color:var(--soft); }\n  .botline b { color:var(--ink); font-variant-numeric:tabular-nums; }\n  .botline .prob { color:var(--faint); font-weight:600; }\n  .hint { color:var(--faint); font-size:12px; line-height:1.5; margin:8px 0 2px; max-width:560px; }\n  .challenge-banner { background:rgba(0,198,90,0.10); border:1px solid var(--green); border-radius:12px; padding:12px 14px; font-size:14px; font-weight:700; color:var(--ink); margin:14px 0 4px; }\n  .vs-friend { font-size:15px; font-weight:800; color:var(--green); margin-top:12px; }\n  .share-row { display:flex; gap:8px; flex-wrap:wrap; align-items:center; margin-top:14px; }\n  .namebox { flex:1; min-width:150px; padding:11px 12px; border:1px solid var(--line); border-radius:10px; font:inherit; }\n  .namebox:focus { outline:2px solid var(--green); border-color:var(--green); }\n  .sharebtn { font:inherit; font-weight:800; color:#fff; background:var(--ink); border:0; border-radius:999px; padding:11px 18px; cursor:pointer; }\n  .sharebtn:hover { filter:brightness(1.15); }\n  .shareurl { width:100%; margin-top:8px; padding:9px 11px; border:1px solid var(--line); border-radius:8px; font-family:ui-monospace,Menlo,monospace; font-size:12px; color:var(--soft); }\n  .proov-credit { margin-top:12px; font-size:11.5px; }\n  .proov-credit a { color:var(--green); font-weight:800; text-decoration:none; }\n  .result { margin-top:10px; font-size:12px; font-weight:700; color:var(--green);\n            border-top:1px dashed var(--line); padding-top:9px; }\n  .scorebtn { margin-top:18px; font:inherit; font-weight:800; font-size:15px; color:#fff;\n              background:var(--green); border:0; border-radius:999px; padding:13px 22px; cursor:pointer; width:100%; }\n  @media (min-width:560px){ .scorebtn { width:auto; } }\n  .
…

Final Submission

Meine Lösung
{"url":"https://radiant-licorice-71148f.netlify.app/","submittedAt":"2026-07-22T11:48:36.609Z"}
Verifiziertes ZertifikatFälschungssicher · ausgestellt von ProoV
1Abgeschlossenes Projekt
1Verifiziertes Zertifikat
92Durchschnittsnote
Erstelle dein Portfolio mit ProoV