Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2cee19ba0e | |||
| 41a623d02a | |||
| 30bc82ad00 | |||
| dd33d45f46 | |||
| cae0d5b946 | |||
| 42fa44c3a2 | |||
| d15d4711e5 | |||
| 61f2a71410 |
1017
AI-Powered Joke Bot _ Student Thesis.html
Normal file
315
AI-Powered Joke Bot _ Student Thesis_files/css2
Normal file
@@ -0,0 +1,315 @@
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2JL7W0Q5n-wU.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa0ZL7W0Q5n-wU.woff2) format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2ZL7W0Q5n-wU.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1pL7W0Q5n-wU.woff2) format('woff2');
|
||||
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2pL7W0Q5n-wU.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa25L7W0Q5n-wU.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1ZL7W0Q5nw.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2JL7W0Q5n-wU.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa0ZL7W0Q5n-wU.woff2) format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2ZL7W0Q5n-wU.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1pL7W0Q5n-wU.woff2) format('woff2');
|
||||
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2pL7W0Q5n-wU.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa25L7W0Q5n-wU.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1ZL7W0Q5nw.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2JL7W0Q5n-wU.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa0ZL7W0Q5n-wU.woff2) format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2ZL7W0Q5n-wU.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1pL7W0Q5n-wU.woff2) format('woff2');
|
||||
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2pL7W0Q5n-wU.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa25L7W0Q5n-wU.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1ZL7W0Q5nw.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2JL7W0Q5n-wU.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa0ZL7W0Q5n-wU.woff2) format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2ZL7W0Q5n-wU.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1pL7W0Q5n-wU.woff2) format('woff2');
|
||||
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2pL7W0Q5n-wU.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa25L7W0Q5n-wU.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1ZL7W0Q5nw.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 800;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2JL7W0Q5n-wU.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 800;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa0ZL7W0Q5n-wU.woff2) format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 800;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2ZL7W0Q5n-wU.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 800;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1pL7W0Q5n-wU.woff2) format('woff2');
|
||||
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 800;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2pL7W0Q5n-wU.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 800;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa25L7W0Q5n-wU.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 800;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1ZL7W0Q5nw.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title>Lesson 1: Battleships in Python!</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background: #f0f8ff;
|
||||
color: #2c3e50;
|
||||
padding: 20px;
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
h1 {
|
||||
color: #2980b9;
|
||||
text-align: center;
|
||||
border-bottom: 3px solid #3498db;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
h2 {
|
||||
color: #2c3e50;
|
||||
margin-top: 25px;
|
||||
}
|
||||
.outcome {
|
||||
background: #e3f2fd;
|
||||
padding: 15px;
|
||||
border-left: 4px solid #3498db;
|
||||
margin: 15px 0;
|
||||
}
|
||||
.why {
|
||||
background: #e8f5e9;
|
||||
padding: 15px;
|
||||
border-left: 4px solid #2ecc71;
|
||||
margin: 15px 0;
|
||||
}
|
||||
.step {
|
||||
background: #fff8e1;
|
||||
padding: 12px;
|
||||
margin: 10px 0;
|
||||
border-radius: 6px;
|
||||
border-left: 4px solid #f39c12;
|
||||
}
|
||||
code {
|
||||
background: #f1f1f1;
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
font-family: monospace;
|
||||
}
|
||||
.time {
|
||||
font-weight: bold;
|
||||
color: #e74c3c;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>🎮 Lesson 1: Find the Hidden Ship!</h1>
|
||||
|
||||
<div class="outcome">
|
||||
<h2>✅ Learning Outcomes</h2>
|
||||
<ul>
|
||||
<li>Use <code>random</code> to create hidden game elements</li>
|
||||
<li>Get input from the player using <code>input()</code></li>
|
||||
<li>Use <code>if</code> / <code>else</code> to give feedback</li>
|
||||
<li>Run and debug a simple Python program</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="why">
|
||||
<h2>💡 Why It Matters</h2>
|
||||
<p>Every video game has <strong>hidden logic</strong> — secret levels, random enemies, or treasure locations. Learning to hide and reveal things with code is the first step to making your own games!</p>
|
||||
<p>These same skills are used in apps, quizzes, and even smart home devices!</p>
|
||||
</div>
|
||||
|
||||
<h2>⏱️ Lesson Plan (40 minutes)</h2>
|
||||
|
||||
<div class="step">
|
||||
<span class="time">0–5 min</span> → <strong>Demo & Explain</strong><br>
|
||||
Show the game: “There’s a secret ship! Can you find it?”<br>
|
||||
Explain: We’ll write code that hides a ship and checks your guess.
|
||||
</div>
|
||||
|
||||
<div class="step">
|
||||
<span class="time">5–20 min</span> → <strong>Code Together</strong><br>
|
||||
Type this starter code (or use your template):
|
||||
<pre style="background:#2d2d2d;color:#f8f8f2;padding:10px;border-radius:6px;font-size:14px;">
|
||||
import random
|
||||
|
||||
ship_row = random.randint(0, 4)
|
||||
ship_col = random.randint(0, 4)
|
||||
|
||||
print("Guess the ship!")
|
||||
guess_row = int(input("Row (0-4): "))
|
||||
guess_col = int(input("Col (0-4): "))
|
||||
|
||||
if guess_row == ship_row and guess_col == ship_col:
|
||||
print("🎯 HIT! You sank the ship!")
|
||||
else:
|
||||
print("💦 MISS!")
|
||||
|
||||
print("The ship was at", ship_row, ship_col)
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div class="step">
|
||||
<span class="time">20–35 min</span> → <strong>Test & Improve</strong><br>
|
||||
• Run the program 3 times<br>
|
||||
• Try to break it (type a letter instead of number — what happens?)<br>
|
||||
• 🌟 <em>Challenge</em>: Add a 2nd ship or limit to 3 guesses!
|
||||
</div>
|
||||
|
||||
<div class="step">
|
||||
<span class="time">35–40 min</span> → <strong>Share & Celebrate</strong><br>
|
||||
Pair up! Can your partner guess the ship in 2 tries?
|
||||
</div>
|
||||
|
||||
<h2>🚀 You Just Learned:</h2>
|
||||
<p>How to create <strong>interactive programs</strong> that respond to user choices — the heart of all games!</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,138 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title>Lesson 2: Hunt All the Ships!</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background: #fff3e0;
|
||||
color: #2c3e50;
|
||||
padding: 20px;
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
h1 {
|
||||
color: #e67e22;
|
||||
text-align: center;
|
||||
border-bottom: 3px solid #f39c12;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
h2 {
|
||||
color: #2c3e50;
|
||||
margin-top: 25px;
|
||||
}
|
||||
.outcome {
|
||||
background: #ffecb3;
|
||||
padding: 15px;
|
||||
border-left: 4px solid #f39c12;
|
||||
margin: 15px 0;
|
||||
}
|
||||
.why {
|
||||
background: #dcedc8;
|
||||
padding: 15px;
|
||||
border-left: 4px solid #8bc34a;
|
||||
margin: 15px 0;
|
||||
}
|
||||
.step {
|
||||
background: #e1f5fe;
|
||||
padding: 12px;
|
||||
margin: 10px 0;
|
||||
border-radius: 6px;
|
||||
border-left: 4px solid #039be5;
|
||||
}
|
||||
code {
|
||||
background: #f1f1f1;
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
font-family: monospace;
|
||||
}
|
||||
.time {
|
||||
font-weight: bold;
|
||||
color: #d32f2f;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>🚢 Lesson 2: Hunt All the Ships!</h1>
|
||||
|
||||
<div class="outcome">
|
||||
<h2>✅ Learning Outcomes</h2>
|
||||
<ul>
|
||||
<li>Use <strong>lists</strong> to store multiple ships</li>
|
||||
<li>Create a <strong>game loop</strong> with turns</li>
|
||||
<li>Track game state (<code>hits</code>, <code>turns</code>)</li>
|
||||
<li>Make decisions with <code>for</code> loops and <code>if</code> inside lists</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="why">
|
||||
<h2>💡 Why It Matters</h2>
|
||||
<p>Real games don’t end after one guess! They track <strong>score</strong>, <strong>lives</strong>, and <strong>progress</strong>. Learning to manage game state is how you build Pac-Man, Minecraft, or Roblox games!</p>
|
||||
<p>Lists and loops are used in <em>every</em> programming language — from apps to robots.</p>
|
||||
</div>
|
||||
|
||||
<h2>⏱️ Lesson Plan (40 minutes)</h2>
|
||||
|
||||
<div class="step">
|
||||
<span class="time">0–5 min</span> → <strong>Review & Goal</strong><br>
|
||||
“Last time: 1 ship. Today: <strong>3 ships</strong> and <strong>10 turns</strong>!”<br>
|
||||
Show the enhanced game in action.
|
||||
</div>
|
||||
|
||||
<div class="step">
|
||||
<span class="time">5–20 min</span> → <strong>Upgrade Your Code</strong><br>
|
||||
Edit your Lesson 1 file to this:
|
||||
<pre style="background:#2d2d2d;color:#f8f8f2;padding:10px;border-radius:6px;font-size:14px;">
|
||||
import random
|
||||
|
||||
ships = []
|
||||
while len(ships) < 3:
|
||||
r = random.randint(0, 4)
|
||||
c = random.randint(0, 4)
|
||||
if [r, c] not in ships:
|
||||
ships.append([r, c])
|
||||
|
||||
print("3 ships hidden! 10 turns to find them all.")
|
||||
hits = 0
|
||||
|
||||
for turn in range(10):
|
||||
print("\nTurn", turn + 1)
|
||||
guess_row = int(input("Row (0-4): "))
|
||||
guess_col = int(input("Col (0-4): "))
|
||||
|
||||
if [guess_row, guess_col] in ships:
|
||||
print("🎯 HIT!")
|
||||
ships.remove([guess_row, guess_col])
|
||||
hits += 1
|
||||
if hits == 3:
|
||||
print("🏆 You win!")
|
||||
break
|
||||
else:
|
||||
print("💦 MISS!")
|
||||
|
||||
if hits < 3:
|
||||
print("Game over! You found", hits, "ships.")
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div class="step">
|
||||
<span class="time">20–35 min</span> → <strong>Level Up!</strong><br>
|
||||
• Test the game (try to win!)<br>
|
||||
• 🌟 <em>Challenge 1</em>: Let players use <strong>A, B, C</strong> for rows!<br>
|
||||
<code>row_letter = input("Row (A-E): ").upper()</code><br>
|
||||
<code>guess_row = ord(row_letter) - ord('A')</code><br>
|
||||
• 🌟 <em>Challenge 2</em>: Show how many turns are left!
|
||||
</div>
|
||||
|
||||
<div class="step">
|
||||
<span class="time">35–40 min</span> → <strong>Play & Reflect</strong><br>
|
||||
Play your friend’s game! What makes it fun?<br>
|
||||
“Now YOU can make games — not just play them!”
|
||||
</div>
|
||||
|
||||
<h2>🌟 You’re Now a Game Coder!</h2>
|
||||
<p>You’ve learned the core ideas behind almost every game: <strong>hidden objects</strong>, <strong>player input</strong>, <strong>feedback</strong>, and <strong>win/lose conditions</strong>.</p>
|
||||
</body>
|
||||
</html>
|
||||
BIN
Battleships/battleships-137-main/.DS_Store
vendored
9
Battleships/battleships-137-main/.gitignore
vendored
@@ -1,9 +0,0 @@
|
||||
core.Microsoft*
|
||||
core.mongo*
|
||||
core.python*
|
||||
env.py
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
node_modules/
|
||||
.github/
|
||||
creds.json
|
||||
@@ -1,84 +0,0 @@
|
||||
FROM gitpod/workspace-base
|
||||
|
||||
RUN echo "CI version from base"
|
||||
|
||||
### NodeJS ###
|
||||
USER gitpod
|
||||
ENV NODE_VERSION=16.13.0
|
||||
ENV TRIGGER_REBUILD=1
|
||||
RUN curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | PROFILE=/dev/null bash \
|
||||
&& bash -c ". .nvm/nvm.sh \
|
||||
&& nvm install $NODE_VERSION \
|
||||
&& nvm use $NODE_VERSION \
|
||||
&& nvm alias default $NODE_VERSION \
|
||||
&& npm install -g typescript yarn node-gyp" \
|
||||
&& echo ". ~/.nvm/nvm.sh" >> /home/gitpod/.bashrc.d/50-node
|
||||
ENV PATH=$PATH:/home/gitpod/.nvm/versions/node/v${NODE_VERSION}/bin
|
||||
|
||||
### Python ###
|
||||
USER gitpod
|
||||
RUN sudo install-packages python3-pip
|
||||
ENV PYTHON_VERSION 3.12.2
|
||||
|
||||
ENV PATH=$HOME/.pyenv/bin:$HOME/.pyenv/shims:$PATH
|
||||
RUN curl -fsSL https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer | bash \
|
||||
&& { echo; \
|
||||
echo 'eval "$(pyenv init -)"'; \
|
||||
echo 'eval "$(pyenv virtualenv-init -)"'; } >> /home/gitpod/.bashrc.d/60-python \
|
||||
&& pyenv update \
|
||||
&& pyenv install $PYTHON_VERSION \
|
||||
&& pyenv global $PYTHON_VERSION \
|
||||
&& python3 -m pip install --no-cache-dir --upgrade pip \
|
||||
&& python3 -m pip install --no-cache-dir --upgrade \
|
||||
setuptools wheel virtualenv pipenv pylint rope flake8 \
|
||||
mypy autopep8 pep8 pylama pydocstyle bandit notebook \
|
||||
twine \
|
||||
&& sudo rm -rf /tmp/*USER gitpod
|
||||
ENV PYTHONUSERBASE=/workspace/.pip-modules \
|
||||
PIP_USER=yes
|
||||
ENV PATH=$PYTHONUSERBASE/bin:$PATH
|
||||
|
||||
# Setup Heroku CLI
|
||||
RUN curl https://cli-assets.heroku.com/install.sh | sh
|
||||
|
||||
RUN wget http://archive.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1f-1ubuntu2_amd64.deb && sudo dpkg -i libssl1.1_1.1.1f-1ubuntu2_amd64.deb && \
|
||||
sudo rm -rf /var/cache/apt/* /var/lib/apt/lists/* /tmp/* /home/gitpod/*.deb && \
|
||||
sudo chown -R gitpod:gitpod /home/gitpod/.cache/heroku/
|
||||
|
||||
# Setup PostgreSQL
|
||||
|
||||
RUN sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" | tee /etc/apt/sources.list.d/pgdg.list' && \
|
||||
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv B97B0AFCAA1A47F044F244A07FCC7D46ACCC4CF8 && \
|
||||
sudo apt-get update -y && \
|
||||
sudo apt-get install -y postgresql-12
|
||||
|
||||
ENV PGDATA="/workspace/.pgsql/data"
|
||||
|
||||
RUN mkdir -p ~/.pg_ctl/bin ~/.pg_ctl/sockets \
|
||||
&& echo '#!/bin/bash\n[ ! -d $PGDATA ] && mkdir -p $PGDATA && initdb --auth=trust -D $PGDATA\npg_ctl -D $PGDATA -l ~/.pg_ctl/log -o "-k ~/.pg_ctl/sockets" start\n' > ~/.pg_ctl/bin/pg_start \
|
||||
&& echo '#!/bin/bash\npg_ctl -D $PGDATA -l ~/.pg_ctl/log -o "-k ~/.pg_ctl/sockets" stop\n' > ~/.pg_ctl/bin/pg_stop \
|
||||
&& chmod +x ~/.pg_ctl/bin/*
|
||||
|
||||
# ENV DATABASE_URL="postgresql://gitpod@localhost"
|
||||
# ENV PGHOSTADDR="127.0.0.1"
|
||||
ENV PGDATABASE="postgres"
|
||||
|
||||
ENV PATH="/usr/lib/postgresql/12/bin:/home/gitpod/.nvm/versions/node/v${NODE_VERSION}/bin:$HOME/.pg_ctl/bin:$PATH"
|
||||
|
||||
|
||||
# Add aliases
|
||||
|
||||
RUN echo 'alias run="python3 $GITPOD_REPO_ROOT/manage.py runserver 0.0.0.0:8000"' >> ~/.bashrc && \
|
||||
echo 'alias heroku_config=". $GITPOD_REPO_ROOT/.vscode/heroku_config.sh"' >> ~/.bashrc && \
|
||||
echo 'alias python=python3' >> ~/.bashrc && \
|
||||
echo 'alias pip=pip3' >> ~/.bashrc && \
|
||||
echo 'alias arctictern="python3 $GITPOD_REPO_ROOT/.vscode/arctictern.py"' >> ~/.bashrc && \
|
||||
echo 'alias font_fix="python3 $GITPOD_REPO_ROOT/.vscode/font_fix.py"' >> ~/.bashrc && \
|
||||
echo 'alias set_pg="export PGHOSTADDR=127.0.0.1"' >> ~/.bashrc && \
|
||||
echo 'alias make_url="python3 $GITPOD_REPO_ROOT/.vscode/make_url.py "' >> ~/.bashrc
|
||||
|
||||
# Local environment variables
|
||||
ENV PORT="8080"
|
||||
ENV IP="0.0.0.0"
|
||||
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
image:
|
||||
file: .gitpod.dockerfile
|
||||
tasks:
|
||||
- init: . ${GITPOD_REPO_ROOT}/.vscode/init_tasks.sh
|
||||
command: /home/gitpod/.pg_ctl/bin/pg_start > /dev/null
|
||||
- command: . ${GITPOD_REPO_ROOT}/.vscode/uptime.sh &
|
||||
vscode:
|
||||
extensions:
|
||||
- ms-python.python
|
||||
- formulahendry.auto-close-tag
|
||||
- eventyret.bootstrap-4-cdn-snippet
|
||||
- hookyqr.beautify
|
||||
- matt-rudge.auto-open-preview-panel
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
modules = ["web", "nodejs-20", "python-3.12"]
|
||||
run = "python3 run.py"
|
||||
|
||||
[nix]
|
||||
channel = "stable-24_05"
|
||||
|
||||
[deployment]
|
||||
run = ["sh", "-c", "python3 run.py"]
|
||||
@@ -1,186 +0,0 @@
|
||||
"""
|
||||
arctictern.py
|
||||
A little script that does a big migration
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import requests
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
from os.path import exists
|
||||
|
||||
COLOURS = {"red": "\033[31m",
|
||||
"blue": "\033[34m",
|
||||
"green": "\033[32m",
|
||||
"reset": "\033[0m",
|
||||
"bold": "\033[1m"}
|
||||
|
||||
BASE_URL = "https://raw.githubusercontent.com/Code-Institute-Org/gitpod-full-template/main/"
|
||||
CURRENT_VERSION = 1.0
|
||||
THIS_VERSION = 1.0
|
||||
|
||||
|
||||
UPGRADE_FILE_LIST = [{"filename": ".vscode/settings.json",
|
||||
"url": ".vscode/settings.json"
|
||||
},
|
||||
{"filename": ".vscode/launch.json",
|
||||
"url": ".vscode/launch.json"
|
||||
},
|
||||
{"filename": ".gitpod.yml",
|
||||
"url": ".gitpod.yml"
|
||||
},
|
||||
{"filename": ".gitpod.dockerfile",
|
||||
"url": ".gitpod.dockerfile"
|
||||
},
|
||||
{"filename": ".vscode/heroku_config.sh",
|
||||
"url": ".vscode/heroku_config.sh"
|
||||
},
|
||||
{"filename": ".vscode/init_tasks.sh",
|
||||
"url": ".vscode/init_tasks.sh"
|
||||
},
|
||||
{"filename": ".vscode/uptime.sh",
|
||||
"url": ".vscode/uptime.sh"
|
||||
},
|
||||
{"filename": ".vscode/make_url.py",
|
||||
"url": ".vscode/make_url.py"
|
||||
},
|
||||
{"filename": ".vscode/arctictern.py",
|
||||
"url": ".vscode/arctictern.py"
|
||||
}]
|
||||
|
||||
FINAL_LINES = "\nexport POST_UPGRADE_RUN=1\nsource ~/.bashrc\n"
|
||||
|
||||
|
||||
def get_versions():
|
||||
|
||||
if exists(".vscode/version.txt"):
|
||||
with open(".vscode/version.txt", "r") as f:
|
||||
THIS_VERSION = float(f.read().strip())
|
||||
else:
|
||||
with open(".vscode/version.txt", "w") as f:
|
||||
f.write(str(THIS_VERSION))
|
||||
|
||||
r = requests.get(BASE_URL + ".vscode/version.txt")
|
||||
CURRENT_VERSION = float(r.content)
|
||||
|
||||
return {"this_version": THIS_VERSION,
|
||||
"current_version": CURRENT_VERSION}
|
||||
|
||||
def needs_upgrade():
|
||||
"""
|
||||
Checks the version of the current template against
|
||||
this version.
|
||||
Returns True if upgrade is needed, False if not.
|
||||
"""
|
||||
|
||||
versions = get_versions()
|
||||
|
||||
print(f"Upstream version: {versions['current_version']}")
|
||||
print(f"Local version: {versions['this_version']}")
|
||||
|
||||
return versions["current_version"] > versions["this_version"]
|
||||
|
||||
|
||||
def write_version():
|
||||
|
||||
versions = get_versions()
|
||||
|
||||
with open(".vscode/version.txt", "w") as f:
|
||||
f.write(str(versions["current_version"]))
|
||||
|
||||
|
||||
def build_post_upgrade():
|
||||
|
||||
r = requests.get(BASE_URL + ".vscode/upgrades.json")
|
||||
upgrades = json.loads(r.content.decode("utf-8"))
|
||||
content = ""
|
||||
|
||||
for k,v in upgrades.items():
|
||||
if float(k) > THIS_VERSION:
|
||||
print(f"Adding version changes for {k} to post_upgrade.sh")
|
||||
content += v
|
||||
|
||||
if content:
|
||||
content += FINAL_LINES
|
||||
with open(".vscode/post_upgrade.sh", "w") as f:
|
||||
f.writelines(content)
|
||||
|
||||
print("Built post_upgrade.sh. Restart your workspace for it to take effect.")
|
||||
|
||||
|
||||
def process(file, suffix):
|
||||
"""
|
||||
Replaces and optionally backs up the files that
|
||||
need to be changed.
|
||||
Arguments: file - a path and filename
|
||||
suffix - the suffix to the BASE_URL
|
||||
"""
|
||||
|
||||
if file == ".gitpod.dockerfile" or file == ".gitpod.yml":
|
||||
try:
|
||||
shutil.copyfile(file, f"{file}.tmp")
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
with open(file, "wb") as f:
|
||||
r = requests.get(BASE_URL + suffix)
|
||||
f.write(r.content)
|
||||
|
||||
if exists(f"{file}.tmp"):
|
||||
result = os.system(f"diff -q {file} {file}.tmp > /dev/null")
|
||||
if result != 0:
|
||||
os.remove(f"{file}.tmp")
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def start_migration():
|
||||
"""
|
||||
Calls the process function and
|
||||
renames the directory
|
||||
"""
|
||||
push_and_recreate = False
|
||||
|
||||
if not os.path.isdir(".vscode"):
|
||||
print("Creating .vscode directory")
|
||||
os.mkdir(".vscode")
|
||||
|
||||
for file in UPGRADE_FILE_LIST:
|
||||
print(f"Processing: {file['filename']}")
|
||||
result = process(file["filename"], file["url"])
|
||||
if result == True:
|
||||
push_and_recreate = True
|
||||
|
||||
if push_and_recreate:
|
||||
write_version()
|
||||
|
||||
if needs_upgrade() and not push_and_recreate:
|
||||
build_post_upgrade()
|
||||
|
||||
print("Changes saved.")
|
||||
print("Please add, commit and push to GitHub.")
|
||||
print("You may need to stop and restart your workspace for")
|
||||
print("the changes to take effect.\n")
|
||||
|
||||
if push_and_recreate:
|
||||
print(f"{COLOURS['red']}{COLOURS['bold']}*** IMPORTANT INFORMATION ***{COLOURS['reset']}")
|
||||
print("The files used to create this workspace have been updated")
|
||||
print("Please download any files that are in .gitignore and")
|
||||
print("recreate this workspace by clicking on the Gitpod button")
|
||||
print("in GitHub. Then, upload your saved files again.\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
print(f"\n🐦 {COLOURS['blue']}{COLOURS['bold']}ArcticTern version 0.3{COLOURS['reset']}")
|
||||
print("CI Template Migration Utility")
|
||||
print("-----------------------------")
|
||||
print("Upgrades the workspace to the latest version.\n")
|
||||
|
||||
if input("Start? Y/N ").lower() == "y":
|
||||
start_migration()
|
||||
else:
|
||||
sys.exit("Migration cancelled by the user")
|
||||
@@ -1,40 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Script to allow Heroku API key to be pasted
|
||||
# exported as an environment variable
|
||||
#
|
||||
# Matt Rudge, May 2021
|
||||
|
||||
echo Heroku authentication configuration script
|
||||
echo Code Institute, 2021
|
||||
echo
|
||||
echo Get your Heroku API key by going to https://dashboard.heroku.com
|
||||
echo Go to Account Settings and click on Reveal to view your Heroku API key
|
||||
echo
|
||||
|
||||
if [[ -z "${HEROKU_API_KEY}" ]]; then
|
||||
echo Paste your Heroku API key here or press Enter to quit:
|
||||
read apikey
|
||||
if [[ -z "${apikey}" ]]; then
|
||||
return 0
|
||||
fi
|
||||
echo export HEROKU_API_KEY=${apikey} >> ~/.bashrc
|
||||
echo Added the export. Refreshing the terminal.
|
||||
. ~/.bashrc > /dev/null
|
||||
echo Done!
|
||||
else
|
||||
echo API key is already set.
|
||||
echo
|
||||
echo To reset the API key please input "'reset'":
|
||||
read reset_trigger
|
||||
if [[ ${reset_trigger} == reset ]]; then
|
||||
unset HEROKU_API_KEY
|
||||
unset reset_trigger
|
||||
echo
|
||||
echo API key removed!
|
||||
else
|
||||
unset reset_trigger
|
||||
echo API key unchanged.
|
||||
fi
|
||||
echo
|
||||
echo Exiting
|
||||
fi
|
||||
@@ -1,13 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Gives a personalised greeting
|
||||
# Adds configuration options for SQLite
|
||||
# Creates run aliases
|
||||
# Author: Matt Rudge
|
||||
|
||||
echo "Setting the greeting"
|
||||
sed -i "s/USER_NAME/$GITPOD_GIT_USER_NAME/g" ${GITPOD_REPO_ROOT}/README.md
|
||||
echo "Creating .sqliterc file"
|
||||
echo ".headers on" > ~/.sqliterc
|
||||
echo ".mode column" >> ~/.sqliterc
|
||||
echo "Your workspace is ready to use. Happy coding!"
|
||||
@@ -1,14 +0,0 @@
|
||||
# Simple utility for creating the Cloudinary URL from a
|
||||
# cloudinary_python.txt file
|
||||
# Matt Rudge, November 2021
|
||||
|
||||
import re
|
||||
|
||||
with open("cloudinary_python.txt") as f:
|
||||
content = f.readlines()
|
||||
|
||||
cloud_name = re.findall(r"['](.*?)[']",content[15])[0]
|
||||
api_key = re.findall(r"['](.*?)[']",content[16])[0]
|
||||
api_secret = re.findall(r"['](.*?)[']",content[17])[0]
|
||||
|
||||
print(f"cloudinary://{api_key}:{api_secret}@{cloud_name}")
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"python.pythonPath": ".venv/bin/python",
|
||||
"python.formatting.provider": "black",
|
||||
"python.formatting.blackArgs": ["--line-length=79" ],
|
||||
"python.formatting.autopep8Args": ["--max-line-length=79" ],
|
||||
"python.linting.enabled": true,
|
||||
"python.linting.flake8Enabled": true,
|
||||
"python.linting.pylintEnabled": true,
|
||||
"python.linting.lintOnSave": true,
|
||||
"python.linting.flake8Args": [
|
||||
"--max-line-length=79"
|
||||
],
|
||||
"python.linting.pylintArgs": [
|
||||
"--disable=C0111" // Disable missing docstring warnings if needed
|
||||
],
|
||||
"files.autoSave": "onFocusChange"
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Pings the webhook so that we can gather
|
||||
# basic usage stats. No personally identifiable
|
||||
# data is captured here, and it is impossible to
|
||||
# identify an individual user from the captured data.
|
||||
# Matt Rudge, April 2021
|
||||
|
||||
UUID=$(cat /proc/sys/kernel/random/uuid)
|
||||
URL=https://1xthkmzwg3.execute-api.eu-west-1.amazonaws.com/prod/lrsapi/
|
||||
API_KEY=jceBCdeGZP9RDeUNCfM4jIQ39Cx0jtG51QgcwDwc
|
||||
VERB="started"
|
||||
|
||||
clear
|
||||
|
||||
while true; do
|
||||
|
||||
DATA="{\"activity_time\":\"$(date +%Y-%m-%dT%H:%M:%S).000Z\",\"actor\":\"${UUID}\",\"verb\":\"${VERB}\",\"activity_object\":\"Gitpod Workspace\",\"extra_data\":\"{}\"}"
|
||||
curl -s -X POST -H "x-api-key: ${API_KEY}" -d "${DATA}" ${URL} 1> /dev/null
|
||||
VERB="running"
|
||||
sleep 300
|
||||
|
||||
done
|
||||
@@ -1 +0,0 @@
|
||||
web: node index.js
|
||||
@@ -1,284 +0,0 @@
|
||||
# [Battleship Game](https://battleships-hedgemonkey-131f157c44ae.herokuapp.com/ "Click to see deployed app")
|
||||
|
||||
This is a simple command-line Battleship game written in Python, with a leader board stored in a google docs spreadsheet.
|
||||
|
||||
## Contents
|
||||
|
||||
<details>
|
||||
<summary>Click here for Table of Contents</summary>
|
||||
|
||||
[Screenshots](#screenshots)
|
||||
|
||||
[Flow Chart Screenshots](#flow-chart-screenshots)
|
||||
|
||||
[Features](#features)
|
||||
|
||||
[Testing](#testing)
|
||||
|
||||
[Future](#future)
|
||||
|
||||
[Deployment](#deployment)
|
||||
|
||||
[Credits](#credits)
|
||||
|
||||
</details>
|
||||
|
||||
## Screenshots
|
||||
| Heroku Deployed App |
|
||||
| :---: |
|
||||
|  |
|
||||
| This is the app showing the Welcome screenwith Leaderboard with scores sourced through Google Docs API |
|
||||
|
||||
## Flow Chart Screenshots
|
||||
| Flow Chart Screenshot |
|
||||
| :---: |
|
||||
|  |
|
||||
| This is a rough flow chart displaying the program processes made using [Milanote](https://milanote.com/)
|
||||
|
||||
[Back to top](#contents)
|
||||
|
||||
## Features
|
||||
* **Player vs. Computer:** Play against a challenging AI opponent.
|
||||
* **Multiple Board Sizes:** Choose a board size between 5x5 and 9x9.
|
||||
* **High Scores:** Track your best scores and compare them to others.
|
||||
* **Colorful Interface:** Enjoy a visually enhanced gameplay experience with colored console output.
|
||||
* **Leaderboard:** Top scores saved to a Google sheets leaderboard using `Google Docs API` and `gspread`
|
||||
|
||||
[Back to top](#contents)
|
||||
|
||||
## Testing
|
||||
|
||||
### Replit
|
||||
Most of this project was developed using Replit and so testing was done as I was building the project step by step.
|
||||
|
||||
### Manual Testing
|
||||
The following manual tests were conducted to ensure the application works as expected:
|
||||
|
||||
#### Test 1: Welcome Message
|
||||
**Steps:**
|
||||
1. Run the application.
|
||||
2. Observe the welcome message displayed in the console.
|
||||
|
||||
**Expected Result:**
|
||||
- The welcome message should be centered and displayed with appropriate colors.
|
||||
|
||||
**Actual Result:**
|
||||
- The welcome message is displayed correctly.
|
||||
|
||||
**Screenshot:**
|
||||

|
||||
|
||||
#### Test 2: High Scores Display
|
||||
**Steps:**
|
||||
1. Run the application.
|
||||
2. Observe the high scores displayed after the welcome message.
|
||||
|
||||
**Expected Result:**
|
||||
- The high scores should be displayed in a centered format with appropriate colors.
|
||||
|
||||
**Actual Result:**
|
||||
- The high scores are displayed correctly.
|
||||
|
||||
**Screenshot:**
|
||||

|
||||
|
||||
#### Test 3: Board Size Input
|
||||
**Steps:**
|
||||
1. Run the application.
|
||||
2. Enter a valid board size (between 5 and 9).
|
||||
|
||||
**Expected Result:**
|
||||
- The application should accept the input and proceed to the next step.
|
||||
|
||||
**Actual Result:**
|
||||
- The application accepts valid inputs and proceeds correctly.
|
||||
|
||||
**Screenshot:**
|
||||

|
||||
|
||||
#### Test 4: Invalid Board Size Input
|
||||
**Steps:**
|
||||
1. Run the application.
|
||||
2. Enter an invalid board size (outside the range of 5 to 9).
|
||||
|
||||
**Expected Result:**
|
||||
- The application should display an error message and prompt for input again.
|
||||
|
||||
**Actual Result:**
|
||||
- The application displays an error message and prompts for input again.
|
||||
|
||||
**Screenshot:**
|
||||

|
||||
|
||||
#### Test 5: Player Turn
|
||||
**Steps:**
|
||||
1. Run the application.
|
||||
2. Enter valid coordinates for the player's turn.
|
||||
|
||||
**Expected Result:**
|
||||
- The application should update the board and display the result (hit or miss).
|
||||
|
||||
**Actual Result:**
|
||||
- The application updates the board and displays the result correctly.
|
||||
|
||||
**Screenshot:**
|
||||

|
||||
|
||||
#### Test 6: Computer Turn
|
||||
**Steps:**
|
||||
1. Run the application.
|
||||
2. Observe the computer's turn.
|
||||
|
||||
**Expected Result:**
|
||||
- The application should update the board and display the result (hit or miss).
|
||||
|
||||
**Actual Result:**
|
||||
- The application updates the board and displays the result correctly.
|
||||
|
||||
**Screenshot:**
|
||||

|
||||
|
||||
#### Test 7: Game Over
|
||||
**Steps:**
|
||||
1. Run the application.
|
||||
2. Play the game until either the player or the computer wins.
|
||||
|
||||
**Expected Result:**
|
||||
- The application should display the game over message and the final scores.
|
||||
|
||||
**Actual Result:**
|
||||
- The application displays the game over message and the final scores correctly.
|
||||
|
||||
**Screenshot:**
|
||||

|
||||
|
||||
#### Test 8: Save High Score
|
||||
**Steps:**
|
||||
1. Run the application.
|
||||
2. Win the game and choose to save the high score.
|
||||
3. Enter a name when prompted.
|
||||
|
||||
**Expected Result:**
|
||||
The application should save the high score to the Google Sheets and display a success message.
|
||||
|
||||
**Actual Result:**
|
||||
The application saves the high score and displays the success message correctly.
|
||||
|
||||
**Empty Username:** Players can intentionally leave the username field empty if they prefer not to submit a name. The high score will still be recorded on the leaderboard with a blank entry for the name.
|
||||
|
||||
**Screenshot:**
|
||||

|
||||
|
||||
[Back to top](#contents)
|
||||
|
||||
### PEP 8 Compliance
|
||||
|
||||
The project's Python code has been refactored to improve adherence to PEP 8 style guidelines. This enhances readability and maintainability. Specific improvements include:
|
||||
|
||||
* **Reduced Branching:** Complex conditional logic, particularly in the `display_boards` function, has been simplified to reduce the number of branches, improving code clarity.
|
||||
* **Modernized String Formatting:** F-strings (formatted string literals) have been adopted throughout the codebase for more concise and readable string formatting.
|
||||
* **Removed Redundant Code:** Unnecessary `else` clauses following `return`, `break`, and `continue` statements have been eliminated.
|
||||
* **Import Order:** Imports have been reorganized to follow PEP 8 recommendations (standard library imports first, followed by third-party and then local imports).
|
||||
|
||||
These changes were guided by feedback from the `pylint` static analysis tool.
|
||||
|
||||

|
||||
|
||||
[Back to top](#contents)
|
||||
|
||||
|
||||
## Future
|
||||
### Potential Future Features
|
||||
* **Player vs. Player:** Perhaps add functionality to allow for a 2 player turn-based mode
|
||||
* **Various Ship Sizes:** It would be nice to add different dized ships more akin to traditional Battleships game, with a choice of orientation
|
||||
* **Number of Ships:** The added functionality to not only choose the board size but to also choose how many ships on the board would also be an attractive addition to the game
|
||||
* **Choose Ship Placement:** The option to decide where you would like to place your ships on the board would also be a nice future feature to incorporate
|
||||
|
||||
[Back to top](#contents)
|
||||
|
||||
## Deployment
|
||||
The site was deployed to Heroku. The steps to deploy are as follows:
|
||||
|
||||
- Set up a [Heroku](https://dashboard.heroku.com) Account and create a new App
|
||||
- In Settings add the python and nodejs buildpacks
|
||||
- In `Settings > Config Vars` Add the creds.json contents under the variable `CREDS`
|
||||
- Link the [GitHub repository](https://github.com/hedgemonkey/battleships) to the Heroku app.
|
||||
|
||||
Heroku git URL
|
||||
[https://git.heroku.com/battleships-hedgemonkey.git]
|
||||
|
||||
The live link can be found [here](https://battleships-hedgemonkey-131f157c44ae.herokuapp.com/)
|
||||
|
||||
[Back to top](#contents)
|
||||
|
||||
### Google Docs
|
||||
This project uses a `Google Sheets` spreadsheet hosted on `Google Docs` accessed by `Google Cloud API` this is to keep track of past scores.
|
||||
|
||||
In order to deploy this yourself you would have to get the correct credentials to access this file or specify your own spreadsheet and provide your own Google Cloud API Credentials
|
||||
|
||||
To do this you ned to save your own credentials to `creds.json` and change the line `SHEET = GSPREAD_CLIENT.open('battleship_scores').sheet1` replacing `battleship_scores` with your own spreadsheet filename
|
||||
|
||||
The spreadsheet can be access [HERE](https://docs.google.com/spreadsheets/d/1cUhnYhy8DuxIEW6_BLnj0OFL38dNoRqmyVvkgnQDai8/edit?usp=sharing)
|
||||
|
||||
[Back to top](#contents)
|
||||
|
||||
### Local Deployment
|
||||
|
||||
You can clone or fork this project to make a local copy on your system.
|
||||
|
||||
[Back to top](#contents)
|
||||
|
||||
#### Cloning
|
||||
|
||||
You can clone the repository by following these steps:
|
||||
|
||||
1. Go to the [GitHub repository](https://github.com/Hedgemonkey/battleships).
|
||||
2. Locate the Code button above the list of files and click it.
|
||||
3. Select if you prefer to clone using HTTPS, SSH, or GitHub CLI and click the copy button to copy the URL to your clipboard.
|
||||
4. Open Git Bash or Terminal.
|
||||
5. Change the current working directory to the one where you want the cloned directory.
|
||||
6. In your IDE Terminal, type the following command to clone my repository:
|
||||
- `git clone https://github.com/hedgemonkey/battleships.git`
|
||||
7. Press Enter to create your local clone.
|
||||
|
||||
[Back to top](#contents)
|
||||
|
||||
#### Forking
|
||||
|
||||
By forking the GitHub Repository, we make a copy of the original repository on our GitHub account to view and/or make changes without affecting the original owner's repository.
|
||||
You can fork this repository by using the following steps:
|
||||
|
||||
1. Log in to GitHub and locate the [GitHub repository](https://github.com/Hedgemonkey/battleships).
|
||||
2. At the top of the Repository (not the top of the page) just above the "Settings" Button on the menu, locate the "Fork" Button.
|
||||
3. Once clicked, you should now have a copy of the original repository in your own GitHub account.
|
||||
|
||||
[Back to top](#contents)
|
||||
|
||||
### Local vs Deployment
|
||||
|
||||
There are no notable differences between my locally developed app and the Heroku deployed site aside from slight variances in the terminal colours.
|
||||
|
||||
[Back to top](#contents)
|
||||
|
||||
|
||||
## Credits
|
||||
|
||||
In this section, I will reference the sources of my content and media, full disclosure of any resources used shall be detailed here.
|
||||
|
||||
- Milanote used to create Flow Chart Diagram
|
||||
|
||||
- Gemini Pro AI Used to help guide me when code wasn't doing as I intended
|
||||
|
||||
[Back to top](#contents)
|
||||
|
||||
### Content
|
||||
|
||||
[Back to top](#contents)
|
||||
|
||||
### Acknowledgements
|
||||
|
||||
- I would like to thank the [Code Institute Slack community](https://code-institute-room.slack.com) for the moral support and general information that helps with my studies.
|
||||
- I would like to also thank Gemini Pro AI for assisting me whenever I needed a bit of quick advice, although it's suggestions often lead to more complications it's explinations and tips were incredibly helpful
|
||||
- YTMusic for providing me with a soundtrack to work to
|
||||
|
||||
[Back to top](#contents)
|
||||
@@ -1,60 +0,0 @@
|
||||
const Pty = require('node-pty');
|
||||
const fs = require('fs');
|
||||
|
||||
exports.install = function () {
|
||||
|
||||
ROUTE('/');
|
||||
WEBSOCKET('/', socket, ['raw']);
|
||||
|
||||
};
|
||||
|
||||
function socket() {
|
||||
|
||||
this.encodedecode = false;
|
||||
this.autodestroy();
|
||||
|
||||
this.on('open', function (client) {
|
||||
|
||||
// Spawn terminal
|
||||
client.tty = Pty.spawn('python3', ['run.py'], {
|
||||
name: 'xterm-color',
|
||||
cols: 80,
|
||||
rows: 24,
|
||||
cwd: process.env.PWD,
|
||||
env: process.env
|
||||
});
|
||||
|
||||
client.tty.on('exit', function (code, signal) {
|
||||
client.tty = null;
|
||||
client.close();
|
||||
console.log("Process killed");
|
||||
});
|
||||
|
||||
client.tty.on('data', function (data) {
|
||||
client.send(data);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
this.on('close', function (client) {
|
||||
if (client.tty) {
|
||||
client.tty.kill(9);
|
||||
client.tty = null;
|
||||
console.log("Process killed and terminal unloaded");
|
||||
}
|
||||
});
|
||||
|
||||
this.on('message', function (client, msg) {
|
||||
client.tty && client.tty.write(msg);
|
||||
});
|
||||
}
|
||||
|
||||
if (process.env.CREDS != null) {
|
||||
console.log("Creating creds.json file.");
|
||||
fs.writeFile('creds.json', process.env.CREDS, 'utf8', function (err) {
|
||||
if (err) {
|
||||
console.log('Error writing file: ', err);
|
||||
socket.emit("console_output", "Error saving credentials: " + err);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
// ===================================================
|
||||
// Total.js start script
|
||||
// https://www.totaljs.com
|
||||
// ===================================================
|
||||
|
||||
const options = {};
|
||||
|
||||
// options.ip = '127.0.0.1';
|
||||
options.port = parseInt(process.env.PORT);
|
||||
// options.unixsocket = require('path').join(require('os').tmpdir(), 'app_name');
|
||||
// options.config = { name: 'Total.js' };
|
||||
// options.sleep = 3000;
|
||||
// options.inspector = 9229;
|
||||
// options.watch = ['private'];
|
||||
// options.livereload = 'https://yourhostname';
|
||||
|
||||
// Enables cluster:
|
||||
// options.cluster = 'auto';
|
||||
// options.cluster_limit = 10; // max 10. threads (works only with "auto" scaling)
|
||||
|
||||
// Enables threads:
|
||||
// options.cluster = 'auto';
|
||||
// options.cluster_limit = 10; // max 10. threads (works only with "auto" scaling)
|
||||
// options.timeout = 5000;
|
||||
// options.threads = '/api/';
|
||||
// options.logs = 'isolated';
|
||||
|
||||
var type = process.argv.indexOf('--release', 1) !== -1 || process.argv.indexOf('release', 1) !== -1 ? 'release' : 'debug';
|
||||
// require('total4/' + type)(options);
|
||||
require('total4').http('release', options);
|
||||
106
Battleships/battleships-137-main/package-lock.json
generated
@@ -1,106 +0,0 @@
|
||||
{
|
||||
"name": "terminal",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "terminal",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"node-pty": "^0.10.1",
|
||||
"node-static": "^0.7.11",
|
||||
"total4": "^0.0.45"
|
||||
}
|
||||
},
|
||||
"node_modules/colors": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
|
||||
"integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.1.90"
|
||||
}
|
||||
},
|
||||
"node_modules/mime": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
|
||||
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"mime": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/minimist": {
|
||||
"version": "0.0.10",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
|
||||
"integrity": "sha512-iotkTvxc+TwOm5Ieim8VnSNvCDjCK9S8G3scJ50ZthspSxa7jx50jkhYduuAtAjvfDUwSgOwf8+If99AlOEhyw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/nan": {
|
||||
"version": "2.20.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.20.0.tgz",
|
||||
"integrity": "sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/node-pty": {
|
||||
"version": "0.10.1",
|
||||
"resolved": "https://registry.npmjs.org/node-pty/-/node-pty-0.10.1.tgz",
|
||||
"integrity": "sha512-JTdtUS0Im/yRsWJSx7yiW9rtpfmxqxolrtnyKwPLI+6XqTAPW/O2MjS8FYL4I5TsMbH2lVgDb2VMjp+9LoQGNg==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"nan": "^2.14.0"
|
||||
}
|
||||
},
|
||||
"node_modules/node-static": {
|
||||
"version": "0.7.11",
|
||||
"resolved": "https://registry.npmjs.org/node-static/-/node-static-0.7.11.tgz",
|
||||
"integrity": "sha512-zfWC/gICcqb74D9ndyvxZWaI1jzcoHmf4UTHWQchBNuNMxdBLJMDiUgZ1tjGLEIe/BMhj2DxKD8HOuc2062pDQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"colors": ">=0.6.0",
|
||||
"mime": "^1.2.9",
|
||||
"optimist": ">=0.3.4"
|
||||
},
|
||||
"bin": {
|
||||
"static": "bin/cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4.1"
|
||||
}
|
||||
},
|
||||
"node_modules/optimist": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
|
||||
"integrity": "sha512-snN4O4TkigujZphWLN0E//nQmm7790RYaE53DdL7ZYwee2D8DDo9/EyYiKUfN3rneWUjhJnueija3G9I2i0h3g==",
|
||||
"license": "MIT/X11",
|
||||
"dependencies": {
|
||||
"minimist": "~0.0.1",
|
||||
"wordwrap": "~0.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/total4": {
|
||||
"version": "0.0.45",
|
||||
"resolved": "https://registry.npmjs.org/total4/-/total4-0.0.45.tgz",
|
||||
"integrity": "sha512-96vXqejddbAeNL6zpzbfm8ztiWL4G0fOuoUWniHDlu6oSz+DcxekHkzK0Y9X+ErlHCRTMsP2+ieMTqG6waowiA==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"total4": "bin/total4"
|
||||
}
|
||||
},
|
||||
"node_modules/wordwrap": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
|
||||
"integrity": "sha512-1tMA907+V4QmxV7dbRvb4/8MaRALK6q9Abid3ndMYnbyo8piisCmeONVqVSXqQA3KaP4SLt5b7ud6E2sqP8TFw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
{
|
||||
"name": "terminal",
|
||||
"version": "1.0.0",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/lechien73/terminal.git"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"bugs": {
|
||||
"url": "https://github.com/lechien73/terminal/issues"
|
||||
},
|
||||
"homepage": "https://github.com/lechien73/terminal#readme",
|
||||
"dependencies": {
|
||||
"node-static": "^0.7.11",
|
||||
"node-pty": "^0.10.1",
|
||||
"total4": "^0.0.45"
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 106 KiB |
|
Before Width: | Height: | Size: 139 KiB |
|
Before Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 51 KiB |
@@ -1,20 +0,0 @@
|
||||
cachetools==5.5.0
|
||||
certifi==2024.8.30
|
||||
cffi==1.17.1
|
||||
charset-normalizer==3.3.2
|
||||
cryptography==43.0.1
|
||||
google-auth==2.34.0
|
||||
google-auth-oauthlib==1.2.1
|
||||
google-oauth==1.0.1
|
||||
gspread==6.1.2
|
||||
idna==3.8
|
||||
oauthlib==3.2.2
|
||||
pyasn1==0.6.0
|
||||
pyasn1_modules==0.4.0
|
||||
pycparser==2.22
|
||||
pyOpenSSL==24.2.1
|
||||
requests==2.32.3
|
||||
requests-oauthlib==2.0.0
|
||||
rsa==4.9
|
||||
six==1.16.0
|
||||
urllib3==2.2.2
|
||||
@@ -1,371 +0,0 @@
|
||||
"""This module is a battleship game"""
|
||||
|
||||
import os
|
||||
import re
|
||||
import string # Corrected import order (see point 14)
|
||||
import random
|
||||
import gspread
|
||||
from google.oauth2.service_account import Credentials
|
||||
|
||||
|
||||
SCOPE = [
|
||||
"https://www.googleapis.com/auth/spreadsheets",
|
||||
"https://www.googleapis.com/auth/drive.file",
|
||||
"https://www.googleapis.com/auth/drive"
|
||||
]
|
||||
|
||||
|
||||
CREDS = Credentials.from_service_account_file('creds.json')
|
||||
SCOPED_CREDS = CREDS.with_scopes(SCOPE)
|
||||
GSPREAD_CLIENT = gspread.authorize(SCOPED_CREDS)
|
||||
SHEET = GSPREAD_CLIENT.open('battleship_scores').sheet1
|
||||
|
||||
|
||||
def center_text(text):
|
||||
"""
|
||||
Function to center text in the console
|
||||
"""
|
||||
console_width = os.get_terminal_size().columns
|
||||
text_length = len(re.sub(r'\x1b\[[0-9;]*m', '', text))
|
||||
padding = (console_width - text_length) // 2
|
||||
return " " * padding + text + " " * padding
|
||||
|
||||
|
||||
def pause():
|
||||
"""
|
||||
Function to pause and clear the screen
|
||||
"""
|
||||
input("\n" + Bcolors.OKCYAN + center_text("Press Enter to continue...") +
|
||||
Bcolors.ENDC)
|
||||
os.system('cls' if os.name == 'nt' else 'clear')
|
||||
|
||||
|
||||
class Bcolors:
|
||||
# pylint: disable=too-few-public-methods
|
||||
"""
|
||||
Colors for console output
|
||||
"""
|
||||
HEADER = '\033[95m'
|
||||
OKBLUE = '\033[34m'
|
||||
OKCYAN = '\033[96m'
|
||||
OKGREEN = '\033[32m'
|
||||
WARNING = '\033[93m'
|
||||
RED = '\033[31m'
|
||||
ENDC = '\033[0m'
|
||||
BOLD = '\033[1m'
|
||||
UNDERLINE = '\033[4m'
|
||||
|
||||
|
||||
def welcome_message():
|
||||
"""
|
||||
Function to display the welcome message with colors
|
||||
"""
|
||||
print(Bcolors.HEADER +
|
||||
Bcolors.BOLD +
|
||||
center_text("Welcome to Battleships!") +
|
||||
Bcolors.ENDC)
|
||||
print("\n" + Bcolors.BOLD +
|
||||
center_text("Welcome to my simple battleships game!") +
|
||||
Bcolors.ENDC)
|
||||
print("\n" + Bcolors.BOLD +
|
||||
center_text("The aim of the game is to hit your opponents ships" +
|
||||
" in the least trys possible!") +
|
||||
Bcolors.ENDC)
|
||||
print("\n" + Bcolors.BOLD +
|
||||
center_text("Just select a gameboard size between a 5x5 grid and a" +
|
||||
" 9x9 grid!") +
|
||||
Bcolors.ENDC)
|
||||
print("\n" + Bcolors.BOLD +
|
||||
center_text("Then enter the co-ordinates of where you want to" +
|
||||
" shoot when prompted!") +
|
||||
Bcolors.ENDC)
|
||||
|
||||
|
||||
def display_high_scores():
|
||||
"""
|
||||
Function to display top 5 high scores
|
||||
"""
|
||||
print("\n" + Bcolors.HEADER + center_text("Current High Scores") +
|
||||
Bcolors.ENDC)
|
||||
print("\n" + Bcolors.OKCYAN + center_text("Top 5 Lowest Misses:") +
|
||||
Bcolors.ENDC)
|
||||
scores = SHEET.get_all_values()
|
||||
sorted_scores = scores[1:]
|
||||
sorted_scores.sort(key=lambda x: int(x[1]))
|
||||
for i in range(1, 6):
|
||||
if i <= len(sorted_scores):
|
||||
print(center_text(f"{i}. {sorted_scores[i-1][0]}: {
|
||||
sorted_scores[i-1][1]}"))
|
||||
else:
|
||||
print(f"{i}. N/A")
|
||||
|
||||
|
||||
def get_board_size():
|
||||
"""
|
||||
Function to get valid board size input
|
||||
"""
|
||||
while True:
|
||||
try:
|
||||
size = int(input("Choose board size (5-9): "))
|
||||
if 5 <= size <= 9:
|
||||
return size
|
||||
print("Invalid size. Please enter a number between 5 and 9")
|
||||
except ValueError:
|
||||
print("Invalid input. Please enter a number.")
|
||||
|
||||
|
||||
def create_boards(size):
|
||||
"""
|
||||
Function to create and initialize boards
|
||||
"""
|
||||
player_board = [['O' for _ in range(size)] for _ in range(size)]
|
||||
computer_board = [['O' for _ in range(size)] for _ in range(size)]
|
||||
return player_board, computer_board
|
||||
|
||||
|
||||
def place_ships(board, num_ships):
|
||||
"""
|
||||
Function to place ships randomly
|
||||
"""
|
||||
ship_locations = []
|
||||
for _ in range(num_ships):
|
||||
while True:
|
||||
row = random.randint(0, len(board) - 1)
|
||||
col = random.randint(0, len(board[0]) - 1)
|
||||
if board[row][col] == 'O':
|
||||
board[row][col] = 'S'
|
||||
ship_locations.append((row, col))
|
||||
break
|
||||
return ship_locations
|
||||
|
||||
|
||||
def display_boards(player_board, computer_board):
|
||||
"""
|
||||
Function to display both boards side by side with colors and spacing
|
||||
"""
|
||||
board_size = len(player_board)
|
||||
header_spacing = board_size - 3 - max(0, board_size - 6)
|
||||
print("\n" + center_text(Bcolors.RED + "Player Board " +
|
||||
" "*header_spacing +
|
||||
Bcolors.ENDC + " | " +
|
||||
Bcolors.RED +
|
||||
" Computer Board" +
|
||||
Bcolors.ENDC))
|
||||
print("\n" + center_text(Bcolors.OKCYAN + " " +
|
||||
" ".join([str(i) for i in range(
|
||||
1, board_size + 1)]) +
|
||||
" " + Bcolors.ENDC +
|
||||
" |" +
|
||||
" " + Bcolors.OKCYAN +
|
||||
" ".join([str(i) for i in range(
|
||||
1, board_size + 1)]) +
|
||||
" " +
|
||||
Bcolors.ENDC))
|
||||
letters = list(string.ascii_uppercase[:board_size])
|
||||
for i in range(board_size):
|
||||
player_row = []
|
||||
computer_row = []
|
||||
for j in range(board_size):
|
||||
if player_board[i][j] == 'X':
|
||||
player_row.append(Bcolors.RED +
|
||||
Bcolors.BOLD +
|
||||
'X' +
|
||||
Bcolors.ENDC)
|
||||
elif player_board[i][j] == 'S':
|
||||
player_row.append(Bcolors.OKBLUE +
|
||||
Bcolors.BOLD +
|
||||
'S' +
|
||||
Bcolors.ENDC)
|
||||
elif player_board[i][j] == 'M':
|
||||
player_row.append(Bcolors.OKGREEN +
|
||||
Bcolors.BOLD +
|
||||
'M' +
|
||||
Bcolors.ENDC)
|
||||
else:
|
||||
player_row.append(player_board[i][j])
|
||||
if computer_board[i][j] == 'X':
|
||||
computer_row.append(Bcolors.RED +
|
||||
Bcolors.BOLD +
|
||||
'X' +
|
||||
Bcolors.ENDC)
|
||||
elif computer_board[i][j] == 'M':
|
||||
computer_row.append(Bcolors.OKGREEN +
|
||||
Bcolors.BOLD +
|
||||
'M' +
|
||||
Bcolors.ENDC)
|
||||
else:
|
||||
computer_row.append('O')
|
||||
player_row_str = ' '.join(player_row)
|
||||
computer_row_str = ' '.join(computer_row)
|
||||
row_str = f"{Bcolors.OKCYAN}{letters[i]: <2} {Bcolors.ENDC}{
|
||||
player_row_str: <{len(player_row_str) + 2}} | {
|
||||
Bcolors.OKCYAN}{
|
||||
letters[i]: <2}{
|
||||
Bcolors.ENDC} {
|
||||
computer_row_str: <{len(computer_row_str) + 2}}"
|
||||
print(center_text(row_str))
|
||||
|
||||
|
||||
def get_target(board):
|
||||
"""
|
||||
Function to get valid target coordinates
|
||||
"""
|
||||
while True:
|
||||
try:
|
||||
row_letter = input(f"Enter row (A-{chr(ord('A') + len(board) - 1)}). ").upper()
|
||||
row = string.ascii_uppercase.index(row_letter.upper())
|
||||
col = int(input(f"Enter column (1-{len(board)}). ")) - 1
|
||||
if 0 <= row < len(board) and 0 <= col < len(board[0]):
|
||||
return row, col
|
||||
print("Invalid coordinates. Please enter numbers within the" +
|
||||
" board size.")
|
||||
except ValueError:
|
||||
print("Invalid input. Please enter letters for Rows and numbers" +
|
||||
" for columns.")
|
||||
|
||||
|
||||
def player_turn(player_board, computer_board, computer_ships, misses):
|
||||
"""
|
||||
Function to handle player's turn
|
||||
"""
|
||||
print(Bcolors.OKBLUE + "\nYour turn:" + Bcolors.ENDC)
|
||||
display_boards(player_board, computer_board)
|
||||
while True:
|
||||
row, col = get_target(computer_board)
|
||||
if computer_board[row][col] == 'M' or computer_board[row][col] == 'X':
|
||||
print(Bcolors.WARNING +
|
||||
"You already tried that target. Choose another one.\n" +
|
||||
Bcolors.ENDC)
|
||||
else:
|
||||
if (row, col) in computer_ships:
|
||||
computer_board[row][col] = 'X'
|
||||
computer_ships.remove((row, col))
|
||||
print(Bcolors.OKGREEN +
|
||||
"Hit! Target: " +
|
||||
chr(ord('A') + row) +
|
||||
" " +
|
||||
str(col + 1) +
|
||||
Bcolors.ENDC)
|
||||
else:
|
||||
computer_board[row][col] = 'M'
|
||||
print(Bcolors.RED +
|
||||
"Miss! Target: " +
|
||||
chr(ord('A') + row) +
|
||||
" " +
|
||||
str(col + 1) +
|
||||
Bcolors.ENDC)
|
||||
misses += 1
|
||||
print(Bcolors.OKCYAN +
|
||||
"\nMisses: " +
|
||||
Bcolors.ENDC +
|
||||
str(misses))
|
||||
break
|
||||
return computer_board, computer_ships, misses
|
||||
|
||||
|
||||
def computer_turn(player_board, player_ships, misses):
|
||||
"""
|
||||
Function to handle computer's turn
|
||||
"""
|
||||
print(Bcolors.OKCYAN + "\nComputer's turn:" + Bcolors.ENDC)
|
||||
while True:
|
||||
row = random.randint(0, len(player_board) - 1)
|
||||
col = random.randint(0, len(player_board[0]) - 1)
|
||||
if player_board[row][col] == 'M' or player_board[row][col] == 'X':
|
||||
continue
|
||||
if (row, col) in player_ships:
|
||||
player_board[row][col] = 'X'
|
||||
player_ships.remove((row, col))
|
||||
print(Bcolors.OKGREEN +
|
||||
"Computer hit! Target: " +
|
||||
chr(ord('A') + row) +
|
||||
" " +
|
||||
str(col + 1) +
|
||||
Bcolors.ENDC)
|
||||
break
|
||||
player_board[row][col] = 'M'
|
||||
print(Bcolors.RED +
|
||||
"Computer missed! Target: " +
|
||||
chr(ord('A') + row) +
|
||||
" " +
|
||||
str(col + 1) +
|
||||
Bcolors.ENDC)
|
||||
misses += 1
|
||||
break
|
||||
pause()
|
||||
return player_board, player_ships, misses
|
||||
|
||||
|
||||
def play_game():
|
||||
"""
|
||||
Function to handle the game loop
|
||||
"""
|
||||
while True:
|
||||
os.system('cl' if os.name == 'nt' else 'clear')
|
||||
welcome_message()
|
||||
display_high_scores()
|
||||
board_size = get_board_size()
|
||||
pause()
|
||||
player_board, computer_board = create_boards(board_size)
|
||||
player_ships = place_ships(player_board, 5)
|
||||
computer_ships = place_ships(computer_board, 5)
|
||||
player_misses = 0
|
||||
computer_misses = 0
|
||||
while player_ships and computer_ships:
|
||||
computer_board, computer_ships, player_misses = player_turn(
|
||||
player_board,
|
||||
computer_board,
|
||||
computer_ships,
|
||||
player_misses)
|
||||
if not computer_ships:
|
||||
print(Bcolors.OKGREEN + "\nCongratulations! You win!" +
|
||||
Bcolors.ENDC)
|
||||
save_to_high_scores(player_misses)
|
||||
break
|
||||
player_board, player_ships, computer_misses = computer_turn(
|
||||
player_board,
|
||||
player_ships,
|
||||
computer_misses)
|
||||
if not player_ships:
|
||||
print(Bcolors.RED + "\nYou lose! Better luck next time." +
|
||||
Bcolors.ENDC)
|
||||
break
|
||||
print("\nFinal Score:")
|
||||
print(f"Player: {player_misses}")
|
||||
print(f"Computer: {computer_misses}")
|
||||
|
||||
while True:
|
||||
play_again = input("\nPlay again? (y/n): ").lower()
|
||||
if play_again in ('y', 'n'):
|
||||
os.system('cls' if os.name == 'nt' else 'clear')
|
||||
break
|
||||
print("Invalid input. Please enter 'y' or 'n'.")
|
||||
if play_again == 'n':
|
||||
print("Goodbye!")
|
||||
break
|
||||
|
||||
|
||||
def save_to_high_scores(misses):
|
||||
"""
|
||||
Function to handle saving scores to the spreadsheet
|
||||
"""
|
||||
while True:
|
||||
save_score = input("Do you want to save your score to the high " +
|
||||
"scores? (y/n): ")
|
||||
if save_score.lower() == 'y':
|
||||
name = input("Enter your name: ")
|
||||
SHEET.append_row([name, misses])
|
||||
os.system('cls' if os.name == 'nt' else 'clear')
|
||||
print("Score saved successfully!")
|
||||
display_high_scores()
|
||||
break
|
||||
if save_score.lower() == 'n':
|
||||
os.system('cls' if os.name == 'nt' else 'clear')
|
||||
print("Score not saved.")
|
||||
display_high_scores()
|
||||
break
|
||||
print("Invalid input. Please enter 'y' or 'n'.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
play_game()
|
||||
@@ -1 +0,0 @@
|
||||
python-3.12.2
|
||||
@@ -1,27 +0,0 @@
|
||||
<body>
|
||||
<button onclick="window.location.reload()">Run Program</button>
|
||||
<div id="terminal"></div>
|
||||
|
||||
<script>
|
||||
var term = new Terminal({
|
||||
cols: 80,
|
||||
rows: 24
|
||||
});
|
||||
term.open(document.getElementById('terminal'));
|
||||
term.writeln('Running startup command: python3 run.py');
|
||||
term.writeln('');
|
||||
|
||||
var ws = new WebSocket(location.protocol.replace('http', 'ws') + '//' + location.hostname + (location.port ? (
|
||||
':' + location.port) : '') + '/');
|
||||
|
||||
ws.onopen = function () {
|
||||
new attach.attach(term, ws);
|
||||
};
|
||||
|
||||
ws.onerror = function (e) {
|
||||
console.log(e);
|
||||
};
|
||||
// Set focus in the terminal
|
||||
document.getElementsByClassName("xterm-helper-textarea")[0].focus();
|
||||
</script>
|
||||
</body>
|
||||
@@ -1,173 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/xterm/3.14.5/xterm.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/xterm/3.14.5/addons/attach/attach.js"></script>
|
||||
<title>Python Terminal by Code Institute</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default styles for xterm.js
|
||||
*/
|
||||
|
||||
.xterm {
|
||||
font-feature-settings: "liga"0;
|
||||
position: relative;
|
||||
user-select: none;
|
||||
-ms-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
.xterm.focus,
|
||||
.xterm:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.xterm .xterm-helpers {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
/**
|
||||
* The z-index of the helpers must be higher than the canvases in order for
|
||||
* IMEs to appear on top.
|
||||
*/
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.xterm .xterm-helper-textarea {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
/* Move textarea out of the screen to the far left, so that the cursor is not visible */
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
left: -9999em;
|
||||
top: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
z-index: -5;
|
||||
/** Prevent wrapping so the IME appears against the textarea at the correct position */
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
.xterm .composition-view {
|
||||
/* TODO: Composition position got messed up somewhere */
|
||||
background: #000;
|
||||
color: #FFF;
|
||||
display: none;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.xterm .composition-view.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.xterm .xterm-viewport {
|
||||
/* On OS X this is required in order for the scroll bar to appear fully opaque */
|
||||
background-color: #000;
|
||||
overflow-y: scroll;
|
||||
cursor: default;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
background-color: rgb(0, 0, 0);
|
||||
width: 730px;
|
||||
}
|
||||
|
||||
.xterm .xterm-screen {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.xterm .xterm-screen canvas {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.xterm .xterm-scroll-area {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.xterm-char-measure-element {
|
||||
display: inline-block;
|
||||
visibility: hidden;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -9999em;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.xterm {
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.xterm.enable-mouse-events {
|
||||
/* When mouse events are enabled (eg. tmux), revert to the standard pointer cursor */
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.xterm.xterm-cursor-pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.xterm.column-select.focus {
|
||||
/* Column selection mode */
|
||||
cursor: crosshair;
|
||||
}
|
||||
|
||||
.xterm .xterm-accessibility,
|
||||
.xterm .xterm-message {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
z-index: 10;
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.xterm .live-region {
|
||||
position: absolute;
|
||||
left: -9999px;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.xterm-dim {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.xterm-underline {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
button {
|
||||
width: 200px;
|
||||
height: 40px;
|
||||
background-color: #E84610;
|
||||
border: 1px solid grey;
|
||||
color: white;
|
||||
text-transform: uppercase;
|
||||
margin: 10px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
@{body}
|
||||
|
||||
</html>
|
||||
@@ -1,41 +0,0 @@
|
||||
# BATTLESHIPS - Grade 7 Python Game
|
||||
# Lesson 2: Full mini-game with 3 ships and turns!
|
||||
|
||||
import random
|
||||
|
||||
# Step 1: Create 3 hidden ships (as a list of [row, col])
|
||||
ships = []
|
||||
while len(ships) < 3:
|
||||
r = random.randint(0, 4)
|
||||
c = random.randint(0, 4)
|
||||
if [r, c] not in ships: # avoid duplicates
|
||||
ships.append([r, c])
|
||||
|
||||
print("3 ships are hidden on a 5x5 grid!")
|
||||
print("You have 10 turns to find them all.")
|
||||
|
||||
hits = 0
|
||||
turns = 10
|
||||
|
||||
# Step 2: Game loop
|
||||
for turn in range(turns):
|
||||
print("\nTurn", turn + 1)
|
||||
|
||||
# Get guess
|
||||
guess_row = int(input("Row (0-4): "))
|
||||
guess_col = int(input("Col (0-4): "))
|
||||
|
||||
# Check if guess is a ship
|
||||
if [guess_row, guess_col] in ships:
|
||||
print("🎯 HIT!")
|
||||
ships.remove([guess_row, guess_col]) # remove found ship
|
||||
hits += 1
|
||||
if hits == 3:
|
||||
print("🏆 You found all ships! You win!")
|
||||
break
|
||||
else:
|
||||
print("💦 MISS!")
|
||||
|
||||
# Step 3: Game over message
|
||||
if hits < 3:
|
||||
print("Game over! You found", hits, "out of 3 ships.")
|
||||
@@ -1,22 +0,0 @@
|
||||
# BATTLESHIPS - Grade 7 Python Game
|
||||
# Lesson 1: Find the hidden ship!
|
||||
|
||||
import random
|
||||
|
||||
# Step 1: Create a secret ship location (row 0-4, col 0-4)
|
||||
ship_row = random.randint(0, 4)
|
||||
ship_col = random.randint(0, 4)
|
||||
|
||||
# Step 2: Ask the player for a guess (we'll improve this later!)
|
||||
print("Guess the ship location!")
|
||||
guess_row = int(input("Row (0-4): "))
|
||||
guess_col = int(input("Col (0-4): "))
|
||||
|
||||
# Step 3: Check if they hit the ship
|
||||
if guess_row == ship_row and guess_col == ship_col:
|
||||
print("🎯 HIT! You sank the ship!")
|
||||
else:
|
||||
print("💦 MISS! Try again.")
|
||||
|
||||
# Step 4: (Optional) Show where the ship really was
|
||||
print("The ship was at row", ship_row, "and col", ship_col)
|
||||
257
Thesis_AI6_Building a Telegram Joke Bot_ A Student Project.html
Normal file
@@ -0,0 +1,257 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Building a Telegram Joke Bot: A Student Project</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Arial', sans-serif;
|
||||
line-height: 1.6;
|
||||
color: #333;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.document {
|
||||
background-color: white;
|
||||
padding: 40px;
|
||||
box-shadow: 0 0 20px rgba(0,0,0,0.1);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #2c3e50;
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
padding-bottom: 15px;
|
||||
border-bottom: 3px solid #3498db;
|
||||
}
|
||||
|
||||
.project-team {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
padding: 15px;
|
||||
background-color: #f0f7ff;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.supervisor {
|
||||
font-weight: bold;
|
||||
color: #2c3e50;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.developers {
|
||||
color: #555;
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: #3498db;
|
||||
margin-top: 25px;
|
||||
margin-bottom: 15px;
|
||||
padding-left: 10px;
|
||||
border-left: 4px solid #3498db;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 15px;
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
ul, ol {
|
||||
margin-bottom: 20px;
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.code-block {
|
||||
background-color: #2c3e50;
|
||||
color: #ecf0f1;
|
||||
padding: 20px;
|
||||
margin: 20px 0;
|
||||
border-radius: 5px;
|
||||
font-family: 'Courier New', monospace;
|
||||
white-space: pre-wrap;
|
||||
overflow-wrap: break-word;
|
||||
font-size: 12px;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.code-note {
|
||||
background-color: #ffebee;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
margin: 10px 0;
|
||||
font-size: 0.9rem;
|
||||
border-left: 3px solid #f44336;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
background-color: #fffacd;
|
||||
padding: 2px 4px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.addendum {
|
||||
background-color: #f9f9f9;
|
||||
padding: 20px;
|
||||
margin-top: 30px;
|
||||
border-left: 4px solid #e74c3c;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
@media print {
|
||||
body {
|
||||
background-color: white;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.document {
|
||||
box-shadow: none;
|
||||
padding: 15mm;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: black;
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: #2c3e50;
|
||||
page-break-after: avoid;
|
||||
}
|
||||
|
||||
.code-block {
|
||||
background-color: #f8f9fa;
|
||||
color: black;
|
||||
border: 1px solid #ddd;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.project-team {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
.addendum {
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="document">
|
||||
<h1>Building a Telegram Joke Bot: A Student Project</h1>
|
||||
|
||||
<div class="project-team">
|
||||
<div class="supervisor">Supervisor: Bob Santos</div>
|
||||
<div class="developers">
|
||||
Developers: Дашунин Дмитрий, Ерохин Даниил, Овганова Инна, Полковников Артём
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Introduction</h2>
|
||||
<p>A group of students created a Telegram joke bot as a school project. A Telegram bot is a small computer program that can send and receive messages automatically. This particular bot tells jokes to people who message it. The students wanted to make something fun that could bring smiles to people's faces while learning about computer programming.</p>
|
||||
|
||||
<h2>Aim</h2>
|
||||
<p>The goal of this project was to create a simple, working bot that could:</p>
|
||||
<ul>
|
||||
<li>Respond to basic commands from users</li>
|
||||
<li>Tell different jokes at random</li>
|
||||
<li>Stay online and working all the time</li>
|
||||
<li>Show how even beginners can create useful programs</li>
|
||||
</ul>
|
||||
|
||||
<h2>Method</h2>
|
||||
<p>The students used these steps to build their bot:</p>
|
||||
<ol>
|
||||
<li><strong>Choose Python</strong>: They picked Python because it's a beginner-friendly programming language.</li>
|
||||
<li><strong>Use Telegram's Tools</strong>: They connected to Telegram's Bot API, which lets programs send and receive messages.</li>
|
||||
<li><strong>Write the Code</strong>: They wrote about 30 lines of code that make the bot work.</li>
|
||||
<li><strong>Add Jokes</strong>: They created a list of three funny jokes for the bot to tell.</li>
|
||||
<li><strong>Test the Bot</strong>: They tested the bot to make sure it worked correctly.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Results</h2>
|
||||
<p>The students successfully created a working Telegram bot with these features:</p>
|
||||
<ul>
|
||||
<li><strong>/start command</strong>: When users type <span class="highlight">/start</span>, the bot greets them and explains how to get jokes.</li>
|
||||
<li><strong>/joke command</strong>: When users type <span class="highlight">/joke</span>, the bot randomly picks and tells one of its jokes.</li>
|
||||
<li><strong>Three different jokes</strong>: The bot can tell jokes about robots, lettuce, and eggs.</li>
|
||||
<li><strong>Always available</strong>: Once started, the bot runs continuously until stopped.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Conclusion</h2>
|
||||
<p>The students learned that creating technology doesn't have to be complicated. With just basic programming skills, they built a bot that can make people happy. This project showed them that coding is not just about math and logic—it's also a creative tool. They proved that even simple programs can have real value and bring joy to people.</p>
|
||||
|
||||
<h2>Future Possibilities</h2>
|
||||
<p>This bot could be improved in many ways:</p>
|
||||
<ul>
|
||||
<li>Let users submit their own jokes</li>
|
||||
<li>Allow users to vote for their favorite jokes</li>
|
||||
<li>Send jokes at scheduled times each day</li>
|
||||
<li>Connect to online joke databases for more variety</li>
|
||||
<li>Add voice messages that tell jokes out loud</li>
|
||||
<li>Tell different types of jokes based on user preferences</li>
|
||||
</ul>
|
||||
|
||||
<div class="addendum">
|
||||
<h2>Addendum: Complete Bot Code</h2>
|
||||
<p>Here is the complete Python code for the Telegram joke bot:</p>
|
||||
|
||||
<div class="code-note">
|
||||
<strong>Note:</strong> For security reasons, the actual bot token has been removed. When using this code, you should replace "YOUR_BOT_TOKEN_HERE" with your actual bot token from @BotFather.
|
||||
</div>
|
||||
|
||||
<div class="code-block">from telegram import Update
|
||||
from telegram.ext import Application, CommandHandler, ContextTypes
|
||||
import random
|
||||
|
||||
JOKE_LIST = [
|
||||
"Why did the robot go to school? To recharge his brain! 🔋",
|
||||
"Knock knock!\nWho's there?\nLettuce!\nLettuce who?\nLettuce in!",
|
||||
"Why don't eggs tell jokes? They'd crack each other up! 🥚"
|
||||
]
|
||||
|
||||
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
await update.message.reply_text("Hi! Type /joke for a funny joke! 😄")
|
||||
|
||||
async def send_joke(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
joke = random.choice(JOKE_LIST)
|
||||
await update.message.reply_text(joke)
|
||||
|
||||
def main():
|
||||
# 🔑 REPLACE THIS with your bot token from @BotFather
|
||||
BOT_TOKEN = "YOUR_BOT_TOKEN_HERE"
|
||||
|
||||
app = Application.builder().token(BOT_TOKEN).build()
|
||||
app.add_handler(CommandHandler("start", start))
|
||||
app.add_handler(CommandHandler("joke", send_joke))
|
||||
print("Bot is running... Press Ctrl+C to stop.")
|
||||
app.run_polling()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()</div>
|
||||
|
||||
<p>The code is organized in a simple way:</p>
|
||||
<ol>
|
||||
<li>It imports necessary tools (like the Telegram library and random number generator)</li>
|
||||
<li>It creates a list of jokes</li>
|
||||
<li>It makes two functions: one for the /start command and one for the /joke command</li>
|
||||
<li>It connects to Telegram using a special bot token</li>
|
||||
<li>It starts the bot and keeps it running</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
BIN
Thesis_AI6_Building a Telegram Joke Bot_ A Student Project.pdf
Normal file
21
android_game.py
Normal file
@@ -0,0 +1,21 @@
|
||||
# asteroid_game.py - Starter Template
|
||||
import turtle
|
||||
import random
|
||||
|
||||
# === SETUP THE GAME WINDOW ===
|
||||
|
||||
|
||||
# === CREATE PLAYER ===
|
||||
|
||||
|
||||
# === GAME VARIABLES ===
|
||||
|
||||
|
||||
# === FUNCTIONS ===
|
||||
|
||||
|
||||
# === TEST CODE ===
|
||||
# Uncomment this to test:
|
||||
# draw_player()
|
||||
# screen.update()
|
||||
# turtle.done()
|
||||
@@ -1,97 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Simple Paddle Game</title>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
background: #222;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
#game {
|
||||
position: relative;
|
||||
width: 400px;
|
||||
height: 300px;
|
||||
border: 2px solid white;
|
||||
background: #000;
|
||||
}
|
||||
#paddle {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
left: 175px;
|
||||
width: 50px;
|
||||
height: 10px;
|
||||
background: white;
|
||||
}
|
||||
#ball {
|
||||
position: absolute;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background: red;
|
||||
border-radius: 50%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="game">
|
||||
<div id="paddle"></div>
|
||||
<div id="ball"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const game = document.getElementById('game');
|
||||
const paddle = document.getElementById('paddle');
|
||||
const ball = document.getElementById('ball');
|
||||
|
||||
let paddleX = 175;
|
||||
let ballX = 200, ballY = 100;
|
||||
let ballSpeedX = 2, ballSpeedY = 2;
|
||||
|
||||
// Move paddle with mouse
|
||||
game.addEventListener('mousemove', (e) => {
|
||||
const rect = game.getBoundingClientRect();
|
||||
paddleX = e.clientX - rect.left - 25;
|
||||
// Keep paddle inside game area
|
||||
if (paddleX < 0) paddleX = 0;
|
||||
if (paddleX > 350) paddleX = 350;
|
||||
paddle.style.left = paddleX + 'px';
|
||||
});
|
||||
|
||||
// Game loop
|
||||
setInterval(() => {
|
||||
// Move ball
|
||||
ballX += ballSpeedX;
|
||||
ballY += ballSpeedY;
|
||||
|
||||
// Bounce off left/right walls
|
||||
if (ballX <= 0 || ballX >= 390) ballSpeedX = -ballSpeedX;
|
||||
// Bounce off top
|
||||
if (ballY <= 0) ballSpeedY = -ballSpeedY;
|
||||
// Bounce off paddle
|
||||
if (
|
||||
ballY >= 280 &&
|
||||
ballX >= paddleX &&
|
||||
ballX <= paddleX + 50
|
||||
) {
|
||||
ballSpeedY = -ballSpeedY;
|
||||
}
|
||||
// Reset if ball falls
|
||||
if (ballY > 300) {
|
||||
ballX = 200;
|
||||
ballY = 100;
|
||||
ballSpeedY = 2;
|
||||
}
|
||||
|
||||
ball.style.left = ballX + 'px';
|
||||
ball.style.top = ballY + 'px';
|
||||
}, 20);
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
535
quiz_game/From_concept_to_release.html
Normal file
@@ -0,0 +1,535 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Build a Russian Millionaire Quiz Game!</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: 'Comic Sans MS', cursive, sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
background: linear-gradient(135deg, #1a2a6c, #b21f1f, #fdbb2d);
|
||||
color: white;
|
||||
min-height: 100vh;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1000px;
|
||||
margin: 0 auto;
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
border-radius: 20px;
|
||||
padding: 30px;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
header {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
padding: 20px;
|
||||
background: linear-gradient(90deg, #1a2a6c, #b21f1f);
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: 10px;
|
||||
text-shadow: 3px 3px 5px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: #fdbb2d;
|
||||
margin: 20px 0 15px;
|
||||
border-bottom: 2px solid #fdbb2d;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.slide {
|
||||
display: none;
|
||||
padding: 20px;
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 15px;
|
||||
margin-bottom: 20px;
|
||||
animation: fadeIn 0.5s ease-in;
|
||||
}
|
||||
|
||||
.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.content {
|
||||
line-height: 1.6;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.role-box {
|
||||
background: linear-gradient(135deg, #1a2a6c, #b21f1f);
|
||||
padding: 15px;
|
||||
border-radius: 10px;
|
||||
margin: 15px 0;
|
||||
border-left: 5px solid #fdbb2d;
|
||||
}
|
||||
|
||||
.role-title {
|
||||
font-weight: bold;
|
||||
color: #fdbb2d;
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
.role-desc {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.btn-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
button {
|
||||
background: linear-gradient(135deg, #1a2a6c, #b21f1f);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 12px 25px;
|
||||
border-radius: 50px;
|
||||
font-size: 1.1rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
button:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
button:active {
|
||||
transform: translateY(1px);
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
height: 10px;
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 5px;
|
||||
margin: 20px 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.progress {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, #1a2a6c, #fdbb2d);
|
||||
width: 0%;
|
||||
transition: width 0.5s;
|
||||
}
|
||||
|
||||
.slide-counter {
|
||||
text-align: center;
|
||||
margin-top: 10px;
|
||||
font-size: 1rem;
|
||||
color: #fdbb2d;
|
||||
}
|
||||
|
||||
.game-preview {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.game-screen {
|
||||
width: 300px;
|
||||
height: 200px;
|
||||
background-color: #1a2a6c;
|
||||
border-radius: 10px;
|
||||
padding: 15px;
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.question-box {
|
||||
background-color: #fdbb2d;
|
||||
color: #1a2a6c;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.option {
|
||||
background-color: white;
|
||||
color: #1a2a6c;
|
||||
padding: 8px;
|
||||
margin: 5px 0;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.option:hover {
|
||||
background-color: #b21f1f;
|
||||
color: white;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
.highlight {
|
||||
color: #fdbb2d;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.russian-flag {
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
height: 15px;
|
||||
background: linear-gradient(to bottom, white 33%, blue 33%, blue 66%, red 66%);
|
||||
margin: 0 5px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header>
|
||||
<h1>Build a Russian Millionaire Quiz Game!</h1>
|
||||
<p>From Concept to Release in 30 Minutes</p>
|
||||
</header>
|
||||
|
||||
<div class="progress-bar">
|
||||
<div class="progress" id="progress"></div>
|
||||
</div>
|
||||
|
||||
<!-- Slide 1: Introduction -->
|
||||
<div class="slide active" id="slide1">
|
||||
<h2>Welcome, Future Game Developers!</h2>
|
||||
<div class="content">
|
||||
<p>Today we're going to create our very own "Who Wants to Be a Millionaire" style quiz game with a Russian twist!</p>
|
||||
<p>We'll use Python Flask for the website and DeepSeek AI to help with our questions.</p>
|
||||
<p>By the end of this class, you'll understand how games are made from start to finish!</p>
|
||||
|
||||
<div class="game-preview">
|
||||
<div class="game-screen">
|
||||
<div class="question-box">What is the capital of Russia?</div>
|
||||
<div class="option">A. St. Petersburg</div>
|
||||
<div class="option">B. Moscow <span class="russian-flag"></span></div>
|
||||
<div class="option">C. Kazan</div>
|
||||
<div class="option">D. Sochi</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p>Let's get started on our game development journey!</p>
|
||||
</div>
|
||||
|
||||
<div class="btn-container">
|
||||
<button onclick="prevSlide()">Previous</button>
|
||||
<button onclick="nextSlide()">Next</button>
|
||||
</div>
|
||||
<div class="slide-counter">Slide 1 of 8</div>
|
||||
</div>
|
||||
|
||||
<!-- Slide 2: Concept & Research -->
|
||||
<div class="slide" id="slide2">
|
||||
<h2>Stage 1: Concept & Research</h2>
|
||||
<div class="content">
|
||||
<p>Every great game starts with an idea! Our concept is:</p>
|
||||
<p class="highlight">"A 'Who Wants to Be a Millionaire' style quiz about Russian culture, history, and fun facts!"</p>
|
||||
|
||||
<p>Our research tasks:</p>
|
||||
<ul>
|
||||
<li>Learn about the original Millionaire game format</li>
|
||||
<li>Research interesting Russian trivia</li>
|
||||
<li>Study how Flask websites work</li>
|
||||
<li>Explore how AI can help create questions</li>
|
||||
</ul>
|
||||
|
||||
<div class="role-box">
|
||||
<div class="role-title">Research Specialist</div>
|
||||
<div class="role-desc">This person will find fun Russian facts and help create our question database.</div>
|
||||
</div>
|
||||
|
||||
<p>Who wants to be our Research Specialist?</p>
|
||||
</div>
|
||||
|
||||
<div class="btn-container">
|
||||
<button onclick="prevSlide()">Previous</button>
|
||||
<button onclick="nextSlide()">Next</button>
|
||||
</div>
|
||||
<div class="slide-counter">Slide 2 of 8</div>
|
||||
</div>
|
||||
|
||||
<!-- Slide 3: User Stories -->
|
||||
<div class="slide" id="slide3">
|
||||
<h2>Stage 2: User Stories</h2>
|
||||
<div class="content">
|
||||
<p>User stories help us understand what our players will do in the game:</p>
|
||||
|
||||
<ul>
|
||||
<li>"As a player, I want to see multiple choice questions"</li>
|
||||
<li>"As a player, I want lifelines like 50:50 and Ask the Audience"</li>
|
||||
<li>"As a player, I want to see my score increase as I answer correctly"</li>
|
||||
<li>"As a player, I want fun Russian-themed graphics and sounds"</li>
|
||||
</ul>
|
||||
|
||||
<p>These stories help us plan what features to build!</p>
|
||||
|
||||
<div class="game-preview">
|
||||
<div class="game-screen">
|
||||
<div class="question-box">Which Russian composer wrote 'Swan Lake'?</div>
|
||||
<div class="option">A. Tchaikovsky <span class="russian-flag"></span></div>
|
||||
<div class="option">B. Rachmaninoff</div>
|
||||
<div class="option">C. Shostakovich</div>
|
||||
<div class="option">D. Stravinsky</div>
|
||||
<div style="margin-top: 10px; font-size: 0.8rem;">
|
||||
Lifelines: 50:50 | Ask AI | Phone a Friend
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="btn-container">
|
||||
<button onclick="prevSlide()">Previous</button>
|
||||
<button onclick="nextSlide()">Next</button>
|
||||
</div>
|
||||
<div class="slide-counter">Slide 3 of 8</div>
|
||||
</div>
|
||||
|
||||
<!-- Slide 4: Task Allocation -->
|
||||
<div class="slide" id="slide4">
|
||||
<h2>Stage 3: Task Allocation</h2>
|
||||
<div class="content">
|
||||
<p>Now let's divide the work! We have 4 important roles:</p>
|
||||
|
||||
<div class="role-box">
|
||||
<div class="role-title">Backend Developer</div>
|
||||
<div class="role-desc">Creates the game logic using Python Flask and connects to the DeepSeek API.</div>
|
||||
</div>
|
||||
|
||||
<div class="role-box">
|
||||
<div class="role-title">Database Designer</div>
|
||||
<div class="role-desc">Builds the question database using JSON and works with the Research Specialist.</div>
|
||||
</div>
|
||||
|
||||
<div class="role-box">
|
||||
<div class="role-title">Frontend Developer</div>
|
||||
<div class="role-desc">Designs the game screens using HTML, CSS, and JavaScript.</div>
|
||||
</div>
|
||||
|
||||
<div class="role-box">
|
||||
<div class="role-title">Graphics & Sound Artist</div>
|
||||
<div class="role-desc">Creates Russian-themed visuals, icons, and sound effects for the game.</div>
|
||||
</div>
|
||||
|
||||
<p>Which role sounds most interesting to you?</p>
|
||||
</div>
|
||||
|
||||
<div class="btn-container">
|
||||
<button onclick="prevSlide()">Previous</button>
|
||||
<button onclick="nextSlide()">Next</button>
|
||||
</div>
|
||||
<div class="slide-counter">Slide 4 of 8</div>
|
||||
</div>
|
||||
|
||||
<!-- Slide 5: Alpha Development -->
|
||||
<div class="slide" id="slide5">
|
||||
<h2>Stage 4: Alpha Development</h2>
|
||||
<div class="content">
|
||||
<p>In the Alpha stage, we build the basic version of our game:</p>
|
||||
|
||||
<ul>
|
||||
<li>Backend Developer: Sets up Flask server and basic game routes</li>
|
||||
<li>Database Designer: Creates JSON file with first 5 Russian trivia questions</li>
|
||||
<li>Frontend Developer: Builds the question screen with 4 answer options</li>
|
||||
<li>Graphics Artist: Creates simple Russian-themed background and buttons</li>
|
||||
</ul>
|
||||
|
||||
<p>Our Alpha version will have:</p>
|
||||
<ul>
|
||||
<li>Basic question display</li>
|
||||
<li>Answer selection</li>
|
||||
<li>Simple scoring</li>
|
||||
<li>Russian color theme (white, blue, red)</li>
|
||||
</ul>
|
||||
|
||||
<p>Let's build our foundation!</p>
|
||||
</div>
|
||||
|
||||
<div class="btn-container">
|
||||
<button onclick="prevSlide()">Previous</button>
|
||||
<button onclick="nextSlide()">Next</button>
|
||||
</div>
|
||||
<div class="slide-counter">Slide 5 of 8</div>
|
||||
</div>
|
||||
|
||||
<!-- Slide 6: Beta Development -->
|
||||
<div class="slide" id="slide6">
|
||||
<h2>Stage 5: Beta Development</h2>
|
||||
<div class="content">
|
||||
<p>In the Beta stage, we add more features and polish:</p>
|
||||
|
||||
<ul>
|
||||
<li>Backend Developer: Implements lifelines and connects to DeepSeek API</li>
|
||||
<li>Database Designer: Expands to 15 questions with difficulty levels</li>
|
||||
<li>Frontend Developer: Adds lifeline buttons and score display</li>
|
||||
<li>Graphics Artist: Creates matryoshka doll icons and Russian landmark images</li>
|
||||
</ul>
|
||||
|
||||
<p>Our Beta version will have:</p>
|
||||
<ul>
|
||||
<li>All 15 questions with increasing difficulty</li>
|
||||
<li>50:50 and "Ask AI" lifelines</li>
|
||||
<li>Better visuals with Russian themes</li>
|
||||
<li>Sound effects for correct/incorrect answers</li>
|
||||
</ul>
|
||||
|
||||
<div class="game-preview">
|
||||
<div class="game-screen">
|
||||
<div class="question-box">What is the traditional Russian doll called?</div>
|
||||
<div class="option">A. Babushka</div>
|
||||
<div class="option">B. Matryoshka <span class="russian-flag"></span></div>
|
||||
<div class="option">C. Krasnaya</div>
|
||||
<div class="option">D. Kukla</div>
|
||||
<div style="margin-top: 10px; font-size: 0.8rem; text-align: center;">
|
||||
Score: 1,000 RUB | Lifelines: <span style="color: #fdbb2d">50:50</span> | Ask AI |
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="btn-container">
|
||||
<button onclick="prevSlide()">Previous</button>
|
||||
<button onclick="nextSlide()">Next</button>
|
||||
</div>
|
||||
<div class="slide-counter">Slide 6 of 8</div>
|
||||
</div>
|
||||
|
||||
<!-- Slide 7: Production & Testing -->
|
||||
<div class="slide" id="slide7">
|
||||
<h2>Stage 6: Production & Testing</h2>
|
||||
<div class="content">
|
||||
<p>Time to test our game and fix any issues!</p>
|
||||
|
||||
<p>Testing tasks:</p>
|
||||
<ul>
|
||||
<li>Play the game multiple times to find bugs</li>
|
||||
<li>Check if all questions display correctly</li>
|
||||
<li>Test the lifelines - do they work properly?</li>
|
||||
<li>Make sure the scoring system is accurate</li>
|
||||
<li>Verify the AI integration gives helpful hints</li>
|
||||
</ul>
|
||||
|
||||
<p>After testing, we'll:</p>
|
||||
<ul>
|
||||
<li>Fix any problems we find</li>
|
||||
<li>Add final polish to graphics and sounds</li>
|
||||
<li>Prepare for our game launch!</li>
|
||||
</ul>
|
||||
|
||||
<p>Who wants to be our Chief Tester?</p>
|
||||
</div>
|
||||
|
||||
<div class="btn-container">
|
||||
<button onclick="prevSlide()">Previous</button>
|
||||
<button onclick="nextSlide()">Next</button>
|
||||
</div>
|
||||
<div class="slide-counter">Slide 7 of 8</div>
|
||||
</div>
|
||||
|
||||
<!-- Slide 8: Release & Celebration -->
|
||||
<div class="slide" id="slide8">
|
||||
<h2>Stage 7: Release & Celebration!</h2>
|
||||
<div class="content">
|
||||
<p>Congratulations! Our Russian Millionaire Quiz Game is ready!</p>
|
||||
|
||||
<p>What we've accomplished:</p>
|
||||
<ul>
|
||||
<li>Created a working quiz game with 15 Russian trivia questions</li>
|
||||
<li>Built a Flask website with game logic</li>
|
||||
<li>Integrated DeepSeek AI for lifelines</li>
|
||||
<li>Designed Russian-themed graphics and sounds</li>
|
||||
<li>Tested and polished our game</li>
|
||||
</ul>
|
||||
|
||||
<p>Now it's time to:</p>
|
||||
<ul>
|
||||
<li>Share our game with friends and family</li>
|
||||
<li>Celebrate our success!</li>
|
||||
<li>Think about what game we want to make next</li>
|
||||
</ul>
|
||||
|
||||
<div class="game-preview">
|
||||
<div class="game-screen" style="text-align: center; display: flex; flex-direction: column; justify-content: center;">
|
||||
<div style="font-size: 1.5rem; color: #fdbb2d; margin-bottom: 10px;">YOU WIN!</div>
|
||||
<div>1,000,000 RUB</div>
|
||||
<div style="margin-top: 15px; font-size: 0.9rem;">Congratulations, Millionaire!</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p>Great job, game developers! You've successfully created your first AI-powered quiz game!</p>
|
||||
</div>
|
||||
|
||||
<div class="btn-container">
|
||||
<button onclick="prevSlide()">Previous</button>
|
||||
<button onclick="restartPresentation()">Start Over</button>
|
||||
</div>
|
||||
<div class="slide-counter">Slide 8 of 8</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let currentSlide = 1;
|
||||
const totalSlides = 8;
|
||||
|
||||
function updateProgress() {
|
||||
const progress = (currentSlide / totalSlides) * 100;
|
||||
document.getElementById('progress').style.width = `${progress}%`;
|
||||
}
|
||||
|
||||
function showSlide(n) {
|
||||
// Hide all slides
|
||||
const slides = document.getElementsByClassName('slide');
|
||||
for (let i = 0; i < slides.length; i++) {
|
||||
slides[i].classList.remove('active');
|
||||
}
|
||||
|
||||
// Show the current slide
|
||||
document.getElementById(`slide${n}`).classList.add('active');
|
||||
|
||||
// Update progress bar
|
||||
updateProgress();
|
||||
}
|
||||
|
||||
function nextSlide() {
|
||||
if (currentSlide < totalSlides) {
|
||||
currentSlide++;
|
||||
showSlide(currentSlide);
|
||||
}
|
||||
}
|
||||
|
||||
function prevSlide() {
|
||||
if (currentSlide > 1) {
|
||||
currentSlide--;
|
||||
showSlide(currentSlide);
|
||||
}
|
||||
}
|
||||
|
||||
function restartPresentation() {
|
||||
currentSlide = 1;
|
||||
showSlide(currentSlide);
|
||||
}
|
||||
|
||||
// Initialize the presentation
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
showSlide(1);
|
||||
updateProgress();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
44
quiz_game/PULL_REQUEST_TEMPLATE.md
Normal file
@@ -0,0 +1,44 @@
|
||||
## Student Submission
|
||||
|
||||
**Name:**
|
||||
**Role:** [Backend/Frontend/Database/Graphics]
|
||||
**Lesson:** [1/2/3]
|
||||
|
||||
## Changes Made
|
||||
|
||||
### Role-Specific Checklist:
|
||||
|
||||
#### Backend Developer (Dima)
|
||||
- [ ] Prize structure implemented (15 levels)
|
||||
- [ ] Game session management
|
||||
- [ ] Answer validation logic
|
||||
- [ ] Lifeline functionality (50:50, Phone Friend)
|
||||
- [ ] All routes working
|
||||
|
||||
#### Frontend Developer (Inna)
|
||||
- [ ] Question display working
|
||||
- [ ] Answer selection handling
|
||||
- [ ] Prize ladder updates
|
||||
- [ ] Lifeline buttons functional
|
||||
- [ ] Game flow complete
|
||||
|
||||
#### Database Designer (Danil)
|
||||
- [ ] 150 Russian culture questions
|
||||
- [ ] Increasing difficulty progression
|
||||
- [ ] Plausible wrong answers
|
||||
- [ ] Categories: history, geography, culture, entertainment
|
||||
|
||||
#### Graphics Artist (Artyom)
|
||||
- [ ] Russian color theme applied
|
||||
- [ ] Enhanced styling from basic template
|
||||
- [ ] Visual improvements
|
||||
- [ ] Visual assets including logo
|
||||
- [ ] Audio assets including sound effects
|
||||
- [ ] Consistent design
|
||||
|
||||
## Testing Performed
|
||||
- [ ] My component works independently
|
||||
- [ ] No errors in console
|
||||
- [ ] Responsive design checked
|
||||
|
||||
## Questions/Blockers
|
||||
1098
quiz_game/Quiz_Game_Project-Student Learning_Portal.html
Normal file
13
quiz_game/README.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# Quizimoto - Russian Millionaire Quiz Game
|
||||
|
||||
## Team Roles:
|
||||
- **Dima**: Backend Developer (app.py)
|
||||
- **Inna**: Frontend Developer (templates/, static/script.js)
|
||||
- **Danil**: Database Designer (questions.json)
|
||||
- **Artyom**: Graphics/UX Designer (static/style.css, visual design)
|
||||
|
||||
## Development Instructions:
|
||||
1. Each team member focuses on their designated files
|
||||
2. Commit and push changes regularly
|
||||
3. Test your components independently
|
||||
4. Collaborate on integration issues
|
||||
876
quiz_game/Russian_Millionaire_Quiz_Game_Comprehensive_Guide.html
Normal file
@@ -0,0 +1,876 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Russian Millionaire Quiz Game - Comprehensive Guide</title>
|
||||
<style>
|
||||
:root {
|
||||
--primary: #1a237e;
|
||||
--secondary: #d32f2f;
|
||||
--accent: #ffc107;
|
||||
--light: #f5f5f5;
|
||||
--dark: #212121;
|
||||
--success: #4caf50;
|
||||
--step-bg: #f8f9fa;
|
||||
--step-border: #dee2e6;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--light);
|
||||
color: var(--dark);
|
||||
line-height: 1.6;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
header {
|
||||
background: linear-gradient(135deg, var(--primary), #283593);
|
||||
color: white;
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
border-radius: 10px 10px 0 0;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.logo-icon {
|
||||
font-size: 3rem;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 1.4rem;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.section {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
||||
margin-bottom: 2rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
background: var(--step-bg);
|
||||
padding: 1.5rem;
|
||||
border-bottom: 2px solid var(--step-border);
|
||||
font-size: 1.3rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.section-content {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.step {
|
||||
background: #f9f9f9;
|
||||
border-left: 4px solid var(--primary);
|
||||
margin: 1rem 0;
|
||||
padding: 1rem;
|
||||
border-radius: 0 8px 8px 0;
|
||||
}
|
||||
|
||||
.step-number {
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
padding: 0.3rem 0.7rem;
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 0.5rem;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.file-structure {
|
||||
background: #fff8e1;
|
||||
padding: 1rem;
|
||||
border-radius: 8px;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.file-item {
|
||||
margin: 0.3rem 0;
|
||||
padding-left: 1.5rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.file-item:before {
|
||||
content: "📄";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.folder:before {
|
||||
content: "📁";
|
||||
}
|
||||
|
||||
.code-block {
|
||||
position: relative;
|
||||
background: #2d2d2d;
|
||||
color: #f8f8f2;
|
||||
border-radius: 8px;
|
||||
padding: 1rem;
|
||||
margin: 1rem 0;
|
||||
overflow-x: auto;
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.code-line {
|
||||
position: relative;
|
||||
padding-left: 4rem;
|
||||
}
|
||||
|
||||
.line-number {
|
||||
position: absolute;
|
||||
left: 1rem;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.tip-box {
|
||||
background: #e8f5e9;
|
||||
padding: 1rem;
|
||||
border-left: 4px solid var(--success);
|
||||
margin: 1rem 0;
|
||||
border-radius: 0 8px 8px 0;
|
||||
}
|
||||
|
||||
.role-header {
|
||||
background: var(--step-bg);
|
||||
padding: 1rem;
|
||||
margin-top: 1.5rem;
|
||||
border-radius: 8px;
|
||||
font-weight: bold;
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
footer {
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
color: #666;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header>
|
||||
<div class="logo">
|
||||
<div class="logo-icon">🎮</div>
|
||||
<div>
|
||||
<h1>Russian Millionaire Quiz Game</h1>
|
||||
<div class="subtitle">Comprehensive Step-by-Step Guide</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-header">Project Overview</div>
|
||||
<div class="section-content">
|
||||
<div class="file-structure">
|
||||
<h4>Project File Structure:</h4>
|
||||
<div class="file-item folder">russian-millionaire/</div>
|
||||
<div class="file-item">app.py (Main application file)</div>
|
||||
<div class="file-item">questions.json (All quiz questions)</div>
|
||||
<div class="file-item folder">templates/</div>
|
||||
<div class="file-item">index.html (Home page)</div>
|
||||
<div class="file-item">game.html (Game screen)</div>
|
||||
<div class="file-item folder">static/</div>
|
||||
<div class="file-item">style.css (All styling)</div>
|
||||
<div class="file-item">script.js (Game interaction)</div>
|
||||
</div>
|
||||
|
||||
<div class="tip-box">
|
||||
<p><strong>Pro Tip:</strong> All project files are available at <a href="https://gitea.techshare.cc/technolyceum/ai6-m2.git" target="_blank">https://gitea.techshare.cc/technolyceum/ai6-m2.git</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-header">Installation Instructions</div>
|
||||
<div class="section-content">
|
||||
<div class="step">
|
||||
<div class="step-number">STEP 1</div>
|
||||
<h3>Clone the Repository</h3>
|
||||
<p>Open your terminal and run the following command to get the project files:</p>
|
||||
<div class="code-block">
|
||||
<div class="code-line">
|
||||
<span class="line-number">1</span>
|
||||
git clone https://gitea.techshare.cc/technolyceum/ai6-m2.git
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="step">
|
||||
<div class="step-number">STEP 2</div>
|
||||
<h3>Navigate to Project Directory</h3>
|
||||
<p>Move into the project directory using:</p>
|
||||
<div class="code-block">
|
||||
<div class="code-line">
|
||||
<span class="line-number">1</span>
|
||||
cd ai6-m2
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="step">
|
||||
<div class="step-number">STEP 3</div>
|
||||
<h3>Install Requirements</h3>
|
||||
<p>Install the necessary Python packages:</p>
|
||||
<div class="code-block">
|
||||
<div class="code-line">
|
||||
<span class="line-number">1</span>
|
||||
pip install -r requirements.txt
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-header">Database Setup - Danil</div>
|
||||
<div class="section-content">
|
||||
<div class="step">
|
||||
<div class="step-number">STEP 1</div>
|
||||
<h3>Open Database File</h3>
|
||||
<p>Navigate to the database file at:</p>
|
||||
<div class="code-block">
|
||||
<div class="code-line">
|
||||
<span class="line-number">1</span>
|
||||
/Users/home/YandexDisk/TECHNOLYCEUM/ict_repos/ai6-m2/starter_templates/docs/roles/database-designer-danil.md
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="step">
|
||||
<div class="step-number">STEP 2</div>
|
||||
<h3>Set Up MongoDB Connection (Lines 1-5)</h3>
|
||||
<p>Add the MongoDB connection information:</p>
|
||||
<div class="code-block">
|
||||
<div class="code-line">
|
||||
<span class="line-number">1</span>
|
||||
// 1. Import MongoDB driver (line 1)
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">2</span>
|
||||
from pymongo import MongoClient
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">3</span>
|
||||
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">4</span>
|
||||
// 2. Connect to MongoDB using the connection URI (line 3-5)
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">5</span>
|
||||
client = MongoClient('mongodb://localhost:27017/')
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">6</span>
|
||||
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">7</span>
|
||||
// 3. Select the appropriate database (line 7)
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">8</span>
|
||||
db = client['student_db']
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">9</span>
|
||||
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">10</span>
|
||||
// 4. Select the questions collection (line 9)
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">11</span>
|
||||
questions_collection = db['questions']
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">12</span>
|
||||
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">13</span>
|
||||
// 5. Add error handling for database connection (line 11)
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">14</span>
|
||||
try:
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">15</span>
|
||||
client.admin.command('ping')
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">16</span>
|
||||
print("Successfully connected to MongoDB!")
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">17</span>
|
||||
except Exception as e:
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">18</span>
|
||||
print(f"MongoDB connection error: {e}")
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="step">
|
||||
<div class="step-number">STEP 3</div>
|
||||
<h3>Insert Questions into Database</h3>
|
||||
<p>Add your quiz questions to the database:</p>
|
||||
<div class="code-block">
|
||||
<div class="code-line">
|
||||
<span class="line-number">1</span>
|
||||
// Sample question format
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">2</span>
|
||||
question = {
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">3</span>
|
||||
// 1. Question text (line 1)
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">4</span>
|
||||
"question": "What is the capital of Russia?",
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">5</span>
|
||||
// 2. Answer options (line 2-5)
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">6</span>
|
||||
"options": ["Moscow", "St. Petersburg", "Novosibirsk", "Kazan"],
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">7</span>
|
||||
// 3. Correct answer index (line 6)
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">8</span>
|
||||
"correct_answer": 0
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">9</span>
|
||||
}
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">10</span>
|
||||
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">11</span>
|
||||
// Insert question into collection (line 8)
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">12</span>
|
||||
questions_collection.insert_one(question)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-header">Backend Setup - Dima</div>
|
||||
<div class="section-content">
|
||||
<div class="step">
|
||||
<div class="step-number">STEP 1</div>
|
||||
<h3>Open Backend File</h3>
|
||||
<p>Navigate to the backend developer documentation:</p>
|
||||
<div class="code-block">
|
||||
<div class="code-line">
|
||||
<span class="line-number">1</span>
|
||||
/Users/home/YandexDisk/TECHNOLYCEUM/ict_repos/ai6-m2/starter_templates/docs/roles/backend-developer-dima.md
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="step">
|
||||
<div class="step-number">STEP 2</div>
|
||||
<h3>Set Up Flask Application (Lines 1-6)</h3>
|
||||
<p>Add the Flask setup code:</p>
|
||||
<div class="code-block">
|
||||
<div class="code-line">
|
||||
<span class="line-number">1</span>
|
||||
// 1. Import Flask and PyMongo (line 1-2)
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">2</span>
|
||||
from flask import Flask, session, redirect, url_for, request, jsonify
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">3</span>
|
||||
from flask_pymongo import PyMongo
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">4</span>
|
||||
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">5</span>
|
||||
// 2. Connect to MongoDB database (line 4-6)
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">6</span>
|
||||
app = Flask(__name__)
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">7</span>
|
||||
app.config['MONGO_URI'] = 'mongodb://localhost:27017/student_db'
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">8</span>
|
||||
mongo = PyMongo(app)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="step">
|
||||
<div class="step-number">STEP 3</div>
|
||||
<h3>Define API Endpoints (Lines 8-14)</h3>
|
||||
<p>Add the API endpoints for getting questions and submitting answers:</p>
|
||||
<div class="code-block">
|
||||
<div class="code-line">
|
||||
<span class="line-number">1</span>
|
||||
// 3. Define API endpoint for getting questions (line 8-10)
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">2</span>
|
||||
@app.route('/get_question')
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">3</span>
|
||||
def get_question():
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">4</span>
|
||||
// 1. Get current question number from session
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">5</span>
|
||||
question_num = session.get('current_question', 0)
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">6</span>
|
||||
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">7</span>
|
||||
// 2. Get question from database
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">8</span>
|
||||
question = mongo.db.questions.find_one({"question_number": question_num})
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">9</span>
|
||||
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">10</span>
|
||||
// 3. Format response
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">11</span>
|
||||
if question:
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">12</span>
|
||||
return jsonify({
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">13</span>
|
||||
"question": question['question'],
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">14</span>
|
||||
"options": question['options'],
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">15</span>
|
||||
"question_number": question_num,
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">16</span>
|
||||
"current_prize": calculate_prize(question_num)
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">17</span>
|
||||
})
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">18</span>
|
||||
else:
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">19</span>
|
||||
return jsonify({"game_over": True, "final_score": calculate_final_score()})
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">20</span>
|
||||
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">21</span>
|
||||
// 4. Define API endpoint for submitting answers (line 12-14)
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">22</span>
|
||||
@app.route('/answer', methods=['POST'])
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">23</span>
|
||||
def answer_question():
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">24</span>
|
||||
// 1. Get user's answer
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">25</span>
|
||||
user_answer = request.json.get('answer')
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">26</span>
|
||||
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">27</span>
|
||||
// 2. Get current question
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">28</span>
|
||||
question_num = session.get('current_question', 0)
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">29</span>
|
||||
current_question = mongo.db.questions.find_one({"question_number": question_num})
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">30</span>
|
||||
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">31</span>
|
||||
// 3. Check if answer is correct
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">32</span>
|
||||
is_correct = current_question['options'][current_question['correct_answer']] == user_answer
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">33</span>
|
||||
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">34</span>
|
||||
// 4. Update game state
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">35</span>
|
||||
if is_correct:
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">36</span>
|
||||
session['current_question'] = question_num + 1
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">37</span>
|
||||
return jsonify({
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">38</span>
|
||||
"correct": True,
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">39</span>
|
||||
"correct_answer": current_question['correct_answer'],
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">40</span>
|
||||
"game_over": question_num + 1 >= TOTAL_QUESTIONS
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">41</span>
|
||||
})
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">42</span>
|
||||
else:
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">43</span>
|
||||
return jsonify({
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">44</span>
|
||||
"correct": False,
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">45</span>
|
||||
"correct_answer": current_question['correct_answer'],
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">46</span>
|
||||
"game_over": True
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">47</span>
|
||||
})
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-header">Frontend Setup - Inna</div>
|
||||
<div class="section-content">
|
||||
<div class="step">
|
||||
<div class="step-number">STEP 1</div>
|
||||
<h3>Open Frontend File</h3>
|
||||
<p>Navigate to the frontend developer documentation:</p>
|
||||
<div class="code-block">
|
||||
<div class="code-line">
|
||||
<span class="line-number">1</span>
|
||||
/Users/home/YandexDisk/TECHNOLYCEUM/ict_repos/ai6-m2/starter_templates/docs/roles/frontend-developer-inna.md
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="step">
|
||||
<div class="step-number">STEP 2</div>
|
||||
<h3>Create HTML Structure (Lines 1-5)</h3>
|
||||
<p>Add the basic HTML structure with game container:</p>
|
||||
<div class="code-block">
|
||||
<div class="code-line">
|
||||
<span class="line-number">1</span>
|
||||
// 1. Create HTML structure with game container (line 1-5)
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">2</span>
|
||||
<!DOCTYPE html>
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">3</span>
|
||||
<html lang="en">
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">4</span>
|
||||
<head>
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">5</span>
|
||||
<meta charset="UTF-8">
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">6</span>
|
||||
<title>Russian Quiz</title>
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">7</span>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">8</span>
|
||||
</head>
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">9</span>
|
||||
<body>
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">10</span>
|
||||
<div class="container">
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">11</span>
|
||||
<h1>Russian Quiz</h1>
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">12</span>
|
||||
<div class="score">Prize: <span id="prize">0</span> ₽</div>
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">13</span>
|
||||
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">14</span>
|
||||
<div class="question-box">
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">15</span>
|
||||
<div class="question-number">Question <span id="q-number">1</span>/5</div>
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">16</span>
|
||||
<div class="question" id="question-text">Loading...</div>
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">17</span>
|
||||
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">18</span>
|
||||
<div class="options" id="options">
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">19</span>
|
||||
<!-- Options go here -->
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">20</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">21</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="step">
|
||||
<div class="step-number">STEP 3</div>
|
||||
<h3>Add Game Controls (Lines 17-20)</h3>
|
||||
<p>Add the game control elements:</p>
|
||||
<div class="code-block">
|
||||
<div class="code-line">
|
||||
<span class="line-number">1</span>
|
||||
// 4. Implement game controls and lifelines (line 17-20)
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">2</span>
|
||||
<button class="lifeline" onclick="useFiftyFifty()">50:50 Lifeline</button>
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">3</span>
|
||||
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">4</span>
|
||||
<div class="result" id="result"></div>
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">5</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">6</span>
|
||||
// 5. Add game over screen UI (line 22-25)
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">7</span>
|
||||
<div class="game-over" id="game-over" style="display: none;">
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">8</span>
|
||||
<h2>Game Over!</h2>
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">9</span>
|
||||
<p>You won: <span id="final-prize">0</span> ₽</p>
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">10</span>
|
||||
<button onclick="restartGame()">Play Again</button>
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">11</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-header">Final Steps</div>
|
||||
<div class="section-content">
|
||||
<div class="step">
|
||||
<div class="step-number">STEP 1</div>
|
||||
<h3>Start MongoDB</h3>
|
||||
<p>Make sure MongoDB is running:</p>
|
||||
<div class="code-block">
|
||||
<div class="code-line">
|
||||
<span class="line-number">1</span>
|
||||
// Start MongoDB service
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">2</span>
|
||||
sudo service mongod start
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="step">
|
||||
<div class="step-number">STEP 2</div>
|
||||
<h3>Run the Application</h3>
|
||||
<p>Start the Flask application:</p>
|
||||
<div class="code-block">
|
||||
<div class="code-line">
|
||||
<span class="line-number">1</span>
|
||||
// Run the app
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="line-number">2</span>
|
||||
python app.py
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="step">
|
||||
<div class="step-number">STEP 3</div>
|
||||
<h3>Access the Game</h3>
|
||||
<p>Open your browser and go to:</p>
|
||||
<div class="code-block">
|
||||
<div class="code-line">
|
||||
<span class="line-number">1</span>
|
||||
http://localhost:5000/start
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<p>© 2025 Technolyceum. All rights reserved.</p>
|
||||
<p>For support, contact: techsupport@technolyceum.cc</p>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
BIN
quiz_game/__pycache__/app.cpython-313.pyc
Normal file
151
quiz_game/app.py
Normal file
@@ -0,0 +1,151 @@
|
||||
from flask import Flask, render_template, request, jsonify, session
|
||||
import json
|
||||
import random
|
||||
from pymongo import MongoClient # Added MongoDB support
|
||||
|
||||
# MongoDB connection
|
||||
MONGO_URI = "mongodb://ai6s3:Student123!@localhost:27017/student_db"
|
||||
client = MongoClient(MONGO_URI)
|
||||
db = client.student_db # Connect to student_db database
|
||||
questions_collection = db.questions # Use questions collection
|
||||
|
||||
app = Flask(__name__)
|
||||
app.secret_key = 'quizimoto_secret_key'
|
||||
|
||||
# Load questions from MongoDB
|
||||
try:
|
||||
questions_data = list(questions_collection.find({}))
|
||||
except Exception as e:
|
||||
print(f"Error loading questions from MongoDB: {e}")
|
||||
questions_data = [] # Fallback to empty list if database connection fails
|
||||
|
||||
# Prize structure (15 levels)
|
||||
PRIZE_LEVELS = [
|
||||
100, 200, 300, 500, 1000,
|
||||
2000, 4000, 8000, 16000, 32000,
|
||||
64000, 125000, 250000, 500000, 1000000
|
||||
]
|
||||
|
||||
GUARANTEED_LEVELS = [5, 10] # First and second guaranteed levels
|
||||
|
||||
def get_current_question():
|
||||
"""Get current question data from session"""
|
||||
current_q_index = session.get('current_question', 0)
|
||||
questions = session.get('questions', [])
|
||||
|
||||
if current_q_index >= len(questions):
|
||||
return None
|
||||
|
||||
return questions[current_q_index], current_q_index
|
||||
|
||||
def calculate_guaranteed_prize(current_index):
|
||||
"""Calculate the guaranteed prize based on current question index"""
|
||||
final_score = 0
|
||||
for level in sorted(GUARANTEED_LEVELS, reverse=True):
|
||||
if current_index + 1 >= level:
|
||||
final_score = PRIZE_LEVELS[level - 1] if level <= len(PRIZE_LEVELS) else 0
|
||||
break
|
||||
return final_score
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
return render_template('index.html')
|
||||
|
||||
@app.route('/start')
|
||||
def start_game():
|
||||
# Initialize game session
|
||||
session['score'] = 0
|
||||
session['current_question'] = 0
|
||||
session['lifelines'] = ['fifty_fifty']
|
||||
session['questions'] = random.sample(questions_data, min(15, len(questions_data))) # Select up to 15 random questions
|
||||
return render_template('game.html')
|
||||
|
||||
@app.route('/get_question')
|
||||
def get_question():
|
||||
current_q_index = session.get('current_question', 0)
|
||||
|
||||
# Check if game is over (15 questions answered or no more questions)
|
||||
if current_q_index >= len(session.get('questions', [])) or current_q_index >= 15:
|
||||
return jsonify({
|
||||
"game_over": True,
|
||||
"final_score": session.get('score', 0)
|
||||
})
|
||||
|
||||
question_data = session['questions'][current_q_index]
|
||||
|
||||
return jsonify({
|
||||
"question_number": current_q_index + 1,
|
||||
"question": question_data['question'],
|
||||
"options": question_data['options'],
|
||||
"current_prize": PRIZE_LEVELS[current_q_index] if current_q_index < len(PRIZE_LEVELS) else 1000000,
|
||||
"game_over": False
|
||||
})
|
||||
|
||||
@app.route('/answer', methods=['POST'])
|
||||
def check_answer():
|
||||
data = request.get_json()
|
||||
answer = data.get('answer', '')
|
||||
|
||||
question_info = get_current_question()
|
||||
if not question_info:
|
||||
return jsonify({"error": "No more questions"})
|
||||
|
||||
question_data, current_q_index = question_info
|
||||
correct = answer == question_data['correct_answer']
|
||||
|
||||
if correct:
|
||||
session['current_q_index'] = current_q_index + 1
|
||||
session['score'] = PRIZE_LEVELS[current_q_index] if current_q_index < len(PRIZE_LEVELS) else session.get('score', 0)
|
||||
|
||||
# Check if game is won (15th question answered correctly)
|
||||
game_won = session['current_question'] >= min(15, len(session.get('questions', [])))
|
||||
|
||||
return jsonify({
|
||||
"correct": True,
|
||||
"correct_answer": question_data['correct_answer'],
|
||||
"final_score": session['score'],
|
||||
"game_over": game_won
|
||||
})
|
||||
else:
|
||||
# Game over with last guaranteed prize
|
||||
final_score = calculate_guaranteed_prize(current_q_index)
|
||||
|
||||
return jsonify({
|
||||
"correct": False,
|
||||
"correct_answer": question_data['correct_answer'],
|
||||
"final_score": final_score,
|
||||
"game_over": True
|
||||
})
|
||||
|
||||
@app.route('/lifeline/<lifeline_name>')
|
||||
def use_lifeline(lifeline_name):
|
||||
if lifeline_name != 'fifty_fifty':
|
||||
return jsonify({"error": "Unknown lifeline"})
|
||||
|
||||
if 'fifty_fifty' not in session.get('lifelines', []):
|
||||
return jsonify({"error": "Lifeline already used"})
|
||||
|
||||
question_info = get_current_question()
|
||||
if not question_info:
|
||||
return jsonify({"error": "No current question"})
|
||||
|
||||
# Remove the lifeline from available lifelines
|
||||
session['lifelines'].remove('fifty_fifty')
|
||||
|
||||
question_data, _ = question_info
|
||||
correct_answer = question_data['correct_answer']
|
||||
options = question_data['options']
|
||||
|
||||
# Find indices of wrong answers to remove (we need to remove 2)
|
||||
wrong_indices = [i for i, option in enumerate(options) if option != correct_answer]
|
||||
|
||||
# Randomly select 2 wrong indices to remove
|
||||
indices_to_remove = random.sample(wrong_indices, min(2, len(wrong_indices)))
|
||||
indices_to_remove.sort(reverse=True) # Sort in descending order for safe removal
|
||||
|
||||
return jsonify({
|
||||
"remove_indices": indices_to_remove
|
||||
})
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(debug=True)
|
||||
BIN
Battleships/.DS_Store → quiz_game/docs/.DS_Store
vendored
19
quiz_game/docs/README.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# Project Documentation
|
||||
|
||||
This directory contains all the documentation for the Millionaire Quiz Game project.
|
||||
|
||||
## Structure
|
||||
|
||||
- [/roles](file:///Users/home/YandexDisk/TECHNOLYCEUM/ict_repos/ai6-m2/docs/roles) - Role-specific instructions and guidelines for team members
|
||||
- [/technical-specs](file:///Users/home/YandexDisk/TECHNOLYCEUM/ict_repos/ai6-m2/docs/technical-specs) - Technical specifications and architecture documents
|
||||
|
||||
## Role Documentation
|
||||
|
||||
Each team member has their own instruction file:
|
||||
|
||||
- [Frontend Developer (Inna)](file:///Users/home/YandexDisk/TECHNOLYCEUM/ict_repos/ai6-m2/docs/roles/frontend-developer-inna.md)
|
||||
- [Backend Developer (Dima)](file:///Users/home/YandexDisk/TECHNOLYCEUM/ict_repos/ai6-m2/docs/roles/backend-developer-dima.md)
|
||||
- [Database Designer (Danil)](file:///Users/home/YandexDisk/TECHNOLYCEUM/ict_repos/ai6-m2/docs/roles/database-designer-danil.md)
|
||||
- [Graphics Designer (Artyom)](file:///Users/home/YandexDisk/TECHNOLYCEUM/ict_repos/ai6-m2/docs/roles/graphics-designer-artyom.md)
|
||||
|
||||
These documents contain step-by-step instructions for implementing various parts of the application.
|
||||
185
quiz_game/docs/roles/backend-developer-dima.md
Normal file
@@ -0,0 +1,185 @@
|
||||
DIMA - BACKEND DEVELOPER MISSION
|
||||
Your Role: Game Brain Developer | Your File: app.py
|
||||
|
||||
💡 What You're Building: You're creating the "brain" of the game - all the rules, scoring, and game logic!
|
||||
|
||||
📋 LESSON 1-2: SETUP & PRIZE SYSTEM
|
||||
|
||||
Step 1: Fork and Clone (10 minutes)
|
||||
1. Go to: https://gitea.techshare.cc/technolyceum/ai6-m2
|
||||
2. Click the "Fork" button (creates your copy)
|
||||
3. Copy your forked repository URL
|
||||
4. Open Terminal and type:
|
||||
git clone [YOUR-FORKED-URL-HERE]
|
||||
cd ai6-m2
|
||||
git checkout -b dima-backend-work
|
||||
|
||||
Step 2: Create the Prize Money System (20 minutes)
|
||||
Open app.py and find these lines around line 13:
|
||||
|
||||
# Prize structure (15 levels)
|
||||
PRIZE_LEVELS = [
|
||||
100, 200, 300, 500, 1000,
|
||||
2000, 4000, 8000, 16000, 32000,
|
||||
64000, 125000, 250000, 500000, 1000000
|
||||
]
|
||||
|
||||
This is already implemented! Notice how the prizes increase as the player progresses through the questions.
|
||||
|
||||
Step 3: Review Game Session Management (15 minutes)
|
||||
Look at the [start_game](file:///Users/home/YandexDisk/TECHNOLYCEUM/ict_repos/ai6-m2/app.py#L42-L48) function (around line 42):
|
||||
|
||||
```python
|
||||
@app.route('/start')
|
||||
def start_game():
|
||||
# Initialize game session
|
||||
session['score'] = 0
|
||||
session['current_question'] = 0
|
||||
session['lifelines'] = ['fifty_fifty']
|
||||
session['questions'] = random.sample(questions_data, min(15, len(questions_data)))
|
||||
return render_template('game.html')
|
||||
```
|
||||
|
||||
This function initializes the game session with:
|
||||
- Starting score of 0
|
||||
- Current question index of 0
|
||||
- One lifeline available: fifty_fifty
|
||||
- A random selection of up to 15 questions from the database
|
||||
|
||||
📋 LESSON 3-4: QUESTION RETRIEVAL & ANSWER VALIDATION
|
||||
|
||||
Step 4: Review Question Retrieval (15 minutes)
|
||||
Check the [get_question](file:///Users/home/YandexDisk/TECHNOLYCEUM/ict_repos/ai6-m2/app.py#L50-L66) function (around line 50):
|
||||
|
||||
```python
|
||||
@app.route('/get_question')
|
||||
def get_question():
|
||||
current_q_index = session.get('current_question', 0)
|
||||
|
||||
# Check if game is over
|
||||
if current_q_index >= min(15, len(session.get('questions', []))):
|
||||
return jsonify({
|
||||
"game_over": True,
|
||||
"final_score": session.get('score', 0)
|
||||
})
|
||||
|
||||
question_data = session['questions'][current_q_index]
|
||||
|
||||
return jsonify({
|
||||
"question_number": current_q_index + 1,
|
||||
"question": question_data['question'],
|
||||
"options": question_data['options'],
|
||||
"current_prize": PRIZE_LEVELS[current_q_index] if current_q_index < len(PRIZE_LEVELS) else 1000000,
|
||||
"game_over": False
|
||||
})
|
||||
```
|
||||
|
||||
This function retrieves the current question and formats it for the frontend.
|
||||
|
||||
Step 5: Review Answer Validation (20 minutes)
|
||||
Look at the [check_answer](file:///Users/home/YandexDisk/TECHNOLYCEUM/ict_repos/ai6-m2/app.py#L68-L103) function (around line 68):
|
||||
|
||||
```python
|
||||
@app.route('/answer', methods=['POST'])
|
||||
def check_answer():
|
||||
data = request.get_json()
|
||||
answer = data.get('answer', '')
|
||||
|
||||
question_info = get_current_question()
|
||||
if not question_info:
|
||||
return jsonify({"error": "No more questions"})
|
||||
|
||||
question_data, current_q_index = question_info
|
||||
correct = answer == question_data['correct_answer']
|
||||
|
||||
if correct:
|
||||
session['current_question'] = current_q_index + 1
|
||||
session['score'] = PRIZE_LEVELS[current_q_index] if current_q_index < len(PRIZE_LEVELS) else session.get('score', 0)
|
||||
|
||||
# Check if game is won (15th question answered correctly)
|
||||
game_won = session['current_question'] >= min(15, len(session.get('questions', [])))
|
||||
|
||||
return jsonify({
|
||||
"correct": True,
|
||||
"correct_answer": question_data['correct_answer'],
|
||||
"final_score": session['score'],
|
||||
"game_over": game_won
|
||||
})
|
||||
else:
|
||||
# Game over with last guaranteed prize
|
||||
final_score = calculate_guaranteed_prize(current_q_index)
|
||||
|
||||
return jsonify({
|
||||
"correct": False,
|
||||
"correct_answer": question_data['correct_answer'],
|
||||
"final_score": final_score,
|
||||
"game_over": True
|
||||
})
|
||||
```
|
||||
|
||||
Notice the helper functions:
|
||||
- [get_current_question()](file:///Users/home/YandexDisk/TECHNOLYCEUM/ict_repos/ai6-m2/app.py#L20-L28) - Gets the current question data from session
|
||||
- [calculate_guaranteed_prize()](file:///Users/home/YandexDisk/TECHNOLYCEUM/ict_repos/ai6-m2/app.py#L30-L37) - Calculates prize based on guaranteed levels
|
||||
|
||||
📋 LESSON 5-6: LIFELINES & REFACTORING
|
||||
|
||||
Step 6: Review Lifeline Implementation (15 minutes)
|
||||
Check the [use_lifeline](file:///Users/home/YandexDisk/TECHNOLYCEUM/ict_repos/ai6-m2/app.py#L105-L133) function (around line 105):
|
||||
|
||||
```python
|
||||
@app.route('/lifeline/<lifeline_name>')
|
||||
def use_lifeline(lifeline_name):
|
||||
if lifeline_name != 'fifty_fifty':
|
||||
return jsonify({"error": "Unknown lifeline"})
|
||||
|
||||
if 'fifty_fifty' not in session.get('lifelines', []):
|
||||
return jsonify({"error": "Lifeline already used"})
|
||||
|
||||
question_info = get_current_question()
|
||||
if not question_info:
|
||||
return jsonify({"error": "No current question"})
|
||||
|
||||
# Remove the lifeline from available lifelines
|
||||
session['lifelines'].remove('fifty_fifty')
|
||||
|
||||
question_data, _ = question_info
|
||||
correct_answer = question_data['correct_answer']
|
||||
options = question_data['options']
|
||||
|
||||
# Find indices of wrong answers to remove (we need to remove 2)
|
||||
wrong_indices = [i for i, option in enumerate(options) if option != correct_answer]
|
||||
|
||||
# Randomly select 2 wrong indices to remove
|
||||
indices_to_remove = random.sample(wrong_indices, min(2, len(wrong_indices)))
|
||||
indices_to_remove.sort(reverse=True) # Sort in descending order for safe removal
|
||||
|
||||
return jsonify({
|
||||
"remove_indices": indices_to_remove
|
||||
})
|
||||
```
|
||||
|
||||
Notice the improvements:
|
||||
- Uses list comprehension for finding wrong indices
|
||||
- Uses helper functions to reduce code duplication
|
||||
- Has clear error handling
|
||||
|
||||
## Database Configuration
|
||||
|
||||
The application should connect to the MongoDB database using the following connection string:
|
||||
```python
|
||||
MONGO_URI = "mongodb://ai6s3:Student123!@localhost:27017/student_db"
|
||||
```
|
||||
|
||||
This connection string should be set in the application's configuration file or environment variables.
|
||||
|
||||
✅ DIMA'S COMPLETION CHECKLIST
|
||||
☐ Understand the prize system structure
|
||||
☐ Review game session initialization
|
||||
☐ Understand question retrieval logic
|
||||
☐ Review answer validation process
|
||||
☐ Understand lifeline implementation
|
||||
☐ Recognize the use of helper functions for DRY code
|
||||
☐ All code pushed to your branch
|
||||
☐ Created Pull Request for teacher to review
|
||||
|
||||
🎉 Congratulations Dima! You built the entire game backend!
|
||||
100
quiz_game/docs/roles/database-designer-danil.md
Normal file
@@ -0,0 +1,100 @@
|
||||
DANIL - DATABASE DESIGNER MISSION
|
||||
Your Role: Question Master | Your File: questions.json
|
||||
|
||||
💡 What You're Building: You're creating all the quiz questions about Russian culture!
|
||||
|
||||
📋 LESSON 1-2: SETUP & BASIC QUESTIONS
|
||||
|
||||
Step 1: Fork and Clone (10 minutes)
|
||||
1. Go to: https://gitea.techshare.cc/technolyceum/ai6-m2
|
||||
2. Click the "Fork" button (creates your copy)
|
||||
3. Copy your forked repository URL
|
||||
4. Open Terminal and type:
|
||||
git clone [YOUR-FORKED-URL-HERE]
|
||||
cd ai6-m2
|
||||
git checkout -b danil-database-work
|
||||
|
||||
Step 2: Review the Question Format (10 minutes)
|
||||
Open questions.json and look at the example:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"question": "What is the capital of Russia?",
|
||||
"options": ["St. Petersburg", "Moscow", "Kazan", "Sochi"],
|
||||
"correct_answer": "Moscow"
|
||||
},
|
||||
{
|
||||
"question": "Which Russian author wrote 'War and Peace'?",
|
||||
"options": ["Dostoevsky", "Tolstoy", "Pushkin", "Chekhov"],
|
||||
"correct_answer": "Tolstoy"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Each question needs:
|
||||
- question: The actual question text
|
||||
- options: 4 possible answers (A, B, C, D)
|
||||
- correct_answer: The right answer (must match exactly one of the options)
|
||||
|
||||
Step 3: Understand the Current Implementation (15 minutes)
|
||||
Notice that the current implementation:
|
||||
- Has properly formatted JSON (no comments)
|
||||
- Has 5 sample questions about Russian culture
|
||||
- Follows the correct structure for integration with the backend
|
||||
|
||||
📋 LESSON 3-4: EXPANDING THE DATABASE
|
||||
|
||||
Step 4: Add More Questions (30 minutes)
|
||||
Add at least 10 more questions to reach a total of 15. Here are some examples:
|
||||
|
||||
```json
|
||||
{
|
||||
"question": "What is the traditional Russian soup made with beets?",
|
||||
"options": ["Shchi", "Borscht", "Solyanka", "Ukha"],
|
||||
"correct_answer": "Borscht"
|
||||
},
|
||||
{
|
||||
"question": "Which Russian ruler was known as 'The Terrible'?",
|
||||
"options": ["Peter I", "Catherine II", "Ivan IV", "Nicholas II"],
|
||||
"correct_answer": "Ivan IV"
|
||||
},
|
||||
{
|
||||
"question": "What is the name of the famous Russian ballet company?",
|
||||
"options": ["Moscow Ballet", "St. Petersburg Ballet", "Bolshoi Ballet", "Russian National Ballet"],
|
||||
"correct_answer": "Bolshoi Ballet"
|
||||
}
|
||||
```
|
||||
|
||||
Requirements for new questions:
|
||||
- Make sure questions cover different aspects of Russian culture (history, literature, food, arts, geography)
|
||||
- Ensure plausible wrong answers that are not obviously incorrect
|
||||
- Verify that the correct_answer exactly matches one of the options
|
||||
|
||||
## MongoDB Connection Instructions
|
||||
|
||||
To connect to the MongoDB database:
|
||||
- URL: `mongodb://ai6s3:Student123!@localhost:27017/student_db`
|
||||
- Username: `ai6s3`
|
||||
- Password: `Student123!`
|
||||
- Database: `student_db`
|
||||
|
||||
To import your JSON data:
|
||||
1. Save your JSON data in a file named `data.json`
|
||||
2. Run the following command in terminal:
|
||||
```bash
|
||||
mongoimport --uri="mongodb://ai6s3:Student123!@localhost:27017/student_db" --collection=questions --type=json --file=data.json --jsonArray
|
||||
```
|
||||
|
||||
Make sure your JSON data format matches the expected schema for the application.
|
||||
|
||||
✅ DANIL'S COMPLETION CHECKLIST
|
||||
☐ Understand the question format
|
||||
☐ Review current implementation
|
||||
☐ Add 10+ more questions about Russian culture
|
||||
☐ Verify all questions follow the correct structure
|
||||
☐ Check that correct_answer matches exactly one option
|
||||
☐ All code pushed to your branch
|
||||
☐ Created Pull Request for teacher to review
|
||||
|
||||
🎉 Congratulations Danil! You built the entire question database!
|
||||
225
quiz_game/docs/roles/frontend-developer-inna.md
Normal file
@@ -0,0 +1,225 @@
|
||||
INNA - FRONTEND DEVELOPER MISSION
|
||||
Your Role: Game Interface Designer | Your Files: templates/ and static/script.js
|
||||
|
||||
💡 What You're Building: You're creating what players see and click on!
|
||||
|
||||
📋 LESSON 1-2: SETUP & BASIC GAME FLOW
|
||||
|
||||
Step 1: Fork and Clone (10 minutes)
|
||||
1. Go to: https://gitea.techshare.cc/technolyceum/ai6-m2
|
||||
2. Click the "Fork" button (creates your copy)
|
||||
3. Copy your forked repository URL
|
||||
4. Open Terminal and type:
|
||||
git clone [YOUR-FORKED-URL-HERE]
|
||||
cd ai6-m2
|
||||
git checkout -b inna-frontend-work
|
||||
|
||||
Step 2: Review Question Loading (15 minutes)
|
||||
Open static/script.js and look at the [loadQuestion](file:///Users/home/YandexDisk/TECHNOLYCEUM/ict_repos/ai6-m2/static/script.js#L9-L19) function:
|
||||
|
||||
```javascript
|
||||
function loadQuestion() {
|
||||
apiRequest('/get_question')
|
||||
.then(data => {
|
||||
if (data.game_over) {
|
||||
endGame(data.final_score);
|
||||
return;
|
||||
}
|
||||
|
||||
currentQuestion = data;
|
||||
displayQuestion(data);
|
||||
})
|
||||
.catch(error => console.error('Error:', error));
|
||||
}
|
||||
```
|
||||
|
||||
Notice how it uses the [apiRequest](file:///Users/home/YandexDisk/TECHNOLYCEUM/ict_repos/ai6-m2/static/script.js#L5-L12) utility function for cleaner code.
|
||||
|
||||
Step 3: Review Question Display (15 minutes)
|
||||
Look at the [displayQuestion](file:///Users/home/YandexDisk/TECHNOLYCEUM/ict_repos/ai6-m2/static/script.js#L21-L41) function:
|
||||
|
||||
```javascript
|
||||
function displayQuestion(data) {
|
||||
updateElementText('q-number', data.question_number);
|
||||
updateElementText('question-text', data.question);
|
||||
updateElementText('prize', data.current_prize);
|
||||
|
||||
const optionsContainer = document.getElementById('options');
|
||||
optionsContainer.innerHTML = '';
|
||||
|
||||
data.options.forEach((option, index) => {
|
||||
const optionElement = document.createElement('div');
|
||||
optionElement.className = 'option';
|
||||
optionElement.textContent = option;
|
||||
optionElement.onclick = () => selectAnswer(option);
|
||||
optionsContainer.appendChild(optionElement);
|
||||
});
|
||||
|
||||
// Reset result display
|
||||
toggleElementVisibility('result', false);
|
||||
const result = document.getElementById('result');
|
||||
if (result) {
|
||||
result.className = 'result';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Notice the utility functions:
|
||||
- [updateElementText()](file:///Users/home/YandexDisk/TECHNOLYCEUM/ict_repos/ai6-m2/static/script.js#L14-L19) - Simplifies updating element text
|
||||
- [toggleElementVisibility()](file:///Users/home/YandexDisk/TECHNOLYCEUM/ict_repos/ai6-m2/static/script.js#L21-L27) - Standardizes showing/hiding elements
|
||||
|
||||
📋 LESSON 3-4: ANSWER HANDLING & LIFELINES
|
||||
|
||||
Step 4: Review Answer Selection (15 minutes)
|
||||
Check the [selectAnswer](file:///Users/home/YandexDisk/TECHNOLYCEUM/ict_repos/ai6-m2/static/script.js#L43-L72) function:
|
||||
|
||||
```javascript
|
||||
function selectAnswer(answer) {
|
||||
apiRequest('/answer', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({ answer: answer })
|
||||
})
|
||||
.then(data => {
|
||||
const result = document.getElementById('result');
|
||||
if (result) {
|
||||
result.style.display = 'block';
|
||||
|
||||
if (data.correct) {
|
||||
result.textContent = 'Correct!';
|
||||
result.className = 'result correct';
|
||||
|
||||
setTimeout(() => {
|
||||
if (data.game_over) {
|
||||
endGame(data.final_score);
|
||||
} else {
|
||||
loadQuestion();
|
||||
}
|
||||
}, 1500);
|
||||
} else {
|
||||
result.textContent = `Wrong! Correct answer: ${data.correct_answer}`;
|
||||
result.className = 'result incorrect';
|
||||
|
||||
setTimeout(() => {
|
||||
endGame(data.final_score);
|
||||
}, 2000);
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(error => console.error('Error:', error));
|
||||
}
|
||||
```
|
||||
|
||||
Step 5: Review Lifeline Implementation (15 minutes)
|
||||
Look at the [useFiftyFifty](file:///Users/home/YandexDisk/TECHNOLYCEUM/ict_repos/ai6-m2/static/script.js#L74-L100) function:
|
||||
|
||||
```javascript
|
||||
function useFiftyFifty() {
|
||||
const lifelineBtn = document.querySelector('.lifeline');
|
||||
if (lifelineBtn) {
|
||||
lifelineBtn.disabled = true;
|
||||
}
|
||||
|
||||
apiRequest('/lifeline/fifty_fifty')
|
||||
.then(data => {
|
||||
if (data.error) {
|
||||
alert(data.error);
|
||||
if (lifelineBtn) {
|
||||
lifelineBtn.disabled = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Hide two wrong options
|
||||
const options = document.querySelectorAll('.option');
|
||||
data.remove_indices.forEach(index => {
|
||||
if (options[index]) {
|
||||
options[index].style.display = 'none';
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
if (lifelineBtn) {
|
||||
lifelineBtn.disabled = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
Notice the improvements:
|
||||
- Uses the [apiRequest](file:///Users/home/YandexDisk/TECHNOLYCEUM/ict_repos/ai6-m2/static/script.js#L5-L12) utility function
|
||||
- Has proper error handling with element checks
|
||||
- Cleaner, more maintainable code
|
||||
|
||||
Step 6: Review Utility Functions (10 minutes)
|
||||
At the top of script.js, notice the utility functions that make the code DRY:
|
||||
|
||||
```javascript
|
||||
// Utility function for making API requests
|
||||
function apiRequest(url, options = {}) {
|
||||
return fetch(url, options)
|
||||
.then(response => response.json())
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
|
||||
// Utility function for updating element text content
|
||||
function updateElementText(id, text) {
|
||||
const element = document.getElementById(id);
|
||||
if (element) {
|
||||
element.textContent = text;
|
||||
}
|
||||
}
|
||||
|
||||
// Utility function for showing/hiding elements
|
||||
function toggleElementVisibility(id, show = true) {
|
||||
const element = document.getElementById(id);
|
||||
if (element) {
|
||||
element.style.display = show ? 'block' : 'none';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
📋 LESSON 5-6: GAME FLOW COMPLETION
|
||||
|
||||
Step 7: Review Game End and Restart (10 minutes)
|
||||
Look at the [endGame](file:///Users/home/YandexDisk/TECHNOLYCEUM/ict_repos/ai6-m2/static/script.js#L102-L108) and [restartGame](file:///Users/home/YandexDisk/TECHNOLYCEUM/ict_repos/ai6-m2/static/script.js#L110-L112) functions:
|
||||
|
||||
```javascript
|
||||
function endGame(score) {
|
||||
updateElementText('final-prize', score);
|
||||
toggleElementVisibility('game-over', true);
|
||||
toggleElementVisibility('question-box', false);
|
||||
toggleElementVisibility('lifeline', false);
|
||||
}
|
||||
|
||||
function restartGame() {
|
||||
window.location.href = '/start';
|
||||
}
|
||||
```
|
||||
|
||||
Notice how they use the utility functions to simplify the code.
|
||||
|
||||
✅ INNA'S COMPLETION CHECKLIST
|
||||
☐ Review question loading implementation
|
||||
☐ Understand question display logic
|
||||
☐ Review answer handling process
|
||||
☐ Understand lifeline implementation
|
||||
☐ Recognize utility functions for DRY code
|
||||
☐ Review game flow completion
|
||||
☐ All code pushed to your branch
|
||||
☐ Created Pull Request for teacher to review
|
||||
|
||||
## Connection Requirements
|
||||
|
||||
The frontend communicates with the backend API, which must be properly configured with the MongoDB connection string:
|
||||
```python
|
||||
MONGO_URI = "mongodb://ai6s3:Student123!@localhost:27017/student_db"
|
||||
```
|
||||
|
||||
Ensure the backend developer has correctly configured this connection string in the application's configuration.
|
||||
|
||||
🎉 Congratulations Inna! You built the entire game interface!
|
||||
319
quiz_game/docs/roles/graphics-designer-artyom.md
Normal file
@@ -0,0 +1,319 @@
|
||||
ARTYOM - GRAPHICS DESIGNER MISSION
|
||||
Your Role: Visual Designer | Your File: static/style.css
|
||||
|
||||
💡 What You're Building: You're making the game look amazing with Russian themes!
|
||||
|
||||
📋 LESSON 1-2: SETUP & RUSSIAN COLOR THEME
|
||||
|
||||
Step 1: Fork and Clone (10 minutes)
|
||||
1. Go to: https://gitea.techshare.cc/technolyceum/ai6-m2
|
||||
2. Click the "Fork" button (creates your copy)
|
||||
3. Copy your forked repository URL
|
||||
4. Open Terminal and type:
|
||||
git clone [YOUR-FORKED-URL-HERE]
|
||||
cd ai6-m2
|
||||
git checkout -b artyom-graphics-work
|
||||
|
||||
Step 2: Enhance the Russian Color Theme (30 minutes)
|
||||
Open static/style.css and find the body section.
|
||||
|
||||
DELETE these lines:
|
||||
|
||||
body {
|
||||
background: #1a2a6c;
|
||||
color: white;
|
||||
min-height: 100vh;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
TYPE THIS instead:
|
||||
|
||||
body {
|
||||
background: linear-gradient(135deg, #1a2a6c, #b21f1f, #fdbb2d);
|
||||
color: white;
|
||||
min-height: 100vh;
|
||||
padding: 20px;
|
||||
font-family: 'Arial', 'Helvetica', sans-serif;
|
||||
}
|
||||
|
||||
Step 3: Make the Header Look Russian (20 minutes)
|
||||
Find the header section.
|
||||
|
||||
DELETE these lines:
|
||||
|
||||
header {
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
padding: 15px;
|
||||
background: #b21f1f;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
TYPE THIS instead:
|
||||
|
||||
/* Keep container styling from current implementation */
|
||||
.container {
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
color: #333;
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #1a237e;
|
||||
text-align: center;
|
||||
font-size: 2.5rem;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
Save and push:
|
||||
git add static/style.css
|
||||
git commit -m "feat: enhanced Russian color theme"
|
||||
git push origin artyom-graphics-work
|
||||
|
||||
📋 LESSON 3-4: GAME ELEMENTS STYLING
|
||||
|
||||
Step 4: Style the Question Container (25 minutes)
|
||||
Find the question-container section.
|
||||
|
||||
DELETE these lines:
|
||||
|
||||
.question-box {
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
color: #333;
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
margin: 15px 0;
|
||||
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.question {
|
||||
font-size: 1.4rem;
|
||||
margin-bottom: 15px;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
TYPE THIS instead:
|
||||
|
||||
.question-container {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
padding: 25px;
|
||||
border-radius: 15px;
|
||||
margin-bottom: 25px;
|
||||
border: 1px solid rgba(253, 187, 45, 0.3);
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.question {
|
||||
font-size: 1.4rem;
|
||||
margin-bottom: 25px;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
color: #fdbb2d;
|
||||
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.question-number {
|
||||
color: #fdbb2d;
|
||||
font-size: 1.1rem;
|
||||
margin-bottom: 15px;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
Step 5: Make Answer Buttons Beautiful (25 minutes)
|
||||
Find the option styling.
|
||||
|
||||
DELETE these lines:
|
||||
|
||||
.option {
|
||||
background: #2196f3;
|
||||
color: white;
|
||||
padding: 12px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
margin: 5px 0;
|
||||
transition: background 0.3s;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.option:hover {
|
||||
background: #1976d2;
|
||||
}
|
||||
|
||||
.option.correct {
|
||||
background: #4caf50;
|
||||
}
|
||||
|
||||
.option.wrong {
|
||||
background: #f44336;
|
||||
}
|
||||
|
||||
TYPE THIS instead:
|
||||
|
||||
.option {
|
||||
background: linear-gradient(135deg, #fdbb2d, #e6a923);
|
||||
color: #1a2a6c;
|
||||
padding: 18px;
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
border: 2px solid #1a2a6c;
|
||||
transition: all 0.3s ease;
|
||||
font-size: 1.1rem;
|
||||
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.option:hover {
|
||||
background: linear-gradient(135deg, #e6a923, #fdbb2d);
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.option:active {
|
||||
transform: translateY(1px);
|
||||
}
|
||||
|
||||
Save and push:
|
||||
git add static/style.css
|
||||
git commit -m "feat: enhanced question and option styling"
|
||||
git push origin artyom-graphics-work
|
||||
|
||||
📋 LESSON 5-6: LIFELINES & FINAL TOUCHES
|
||||
|
||||
Step 6: Style Lifelines and Game Over Screen (40 minutes)
|
||||
Find the lifelines section.
|
||||
|
||||
DELETE these lines:
|
||||
|
||||
.lifelines {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.lifeline-buttons {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.lifeline-btn {
|
||||
background: #1a2a6c;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 10px 15px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
TYPE THIS instead:
|
||||
|
||||
.result.correct {
|
||||
background: #c8e6c9;
|
||||
color: #2e7d32;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.result.incorrect {
|
||||
background: #ffcdd2;
|
||||
color: #c62828;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.lifeline {
|
||||
background: #ff9800;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 12px 20px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
margin: 15px auto;
|
||||
font-weight: bold;
|
||||
transition: background 0.3s;
|
||||
}
|
||||
|
||||
.lifeline:hover {
|
||||
background: #f57c00;
|
||||
}
|
||||
|
||||
.lifeline:disabled {
|
||||
background: #ccc;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
Step 7: Make Game Over Screen Epic (20 minutes)
|
||||
Find the game-over-screen section.
|
||||
|
||||
DELETE these lines:
|
||||
|
||||
.game-over-screen {
|
||||
text-align: center;
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
TYPE THIS instead:
|
||||
|
||||
.game-over-screen {
|
||||
text-align: center;
|
||||
padding: 30px;
|
||||
background: white;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
.game-over-screen h2 {
|
||||
color: #d32f2f;
|
||||
font-size: 2rem;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.game-over-screen p {
|
||||
font-size: 1.2rem;
|
||||
margin-bottom: 20px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.game-over-screen button {
|
||||
background: #d32f2f;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 12px 24px;
|
||||
border-radius: 5px;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
transition: background 0.3s;
|
||||
}
|
||||
|
||||
.game-over-screen button:hover {
|
||||
background: #b71c1c;
|
||||
}
|
||||
|
||||
Final push:
|
||||
git add static/style.css
|
||||
git commit -m "feat: completed Russian-themed visual design"
|
||||
git push origin artyom-graphics-work
|
||||
|
||||
✅ ARTYOM'S COMPLETION CHECKLIST
|
||||
☐ Russian flag colors used throughout (white, blue, red, gold)
|
||||
☐ Gradient backgrounds look professional
|
||||
☐ Buttons have hover effects and animations
|
||||
☐ Game elements have shadows and borders
|
||||
☐ Text is readable with good contrast
|
||||
☐ Game over screen looks exciting
|
||||
☐ All code pushed to your branch
|
||||
☐ Created Pull Request for teacher to review
|
||||
|
||||
🎉 Congratulations Artyom! You made the game look amazing!
|
||||
262
quiz_game/docs/technical-specs/teacher-project-guide.md
Normal file
@@ -0,0 +1,262 @@
|
||||
# Teacher - Complete Project Manager Guide
|
||||
|
||||
## YOUR ROLES:
|
||||
- Team Lead - Guide students through development
|
||||
- Git Master - Manage repositories and merges
|
||||
- User Tester - Verify all components work
|
||||
- Integration Manager - Combine all student work
|
||||
|
||||
## PROJECT TIMELINE (6 Lessons)
|
||||
|
||||
=========================================
|
||||
|
||||
## LESSON 1-2: SETUP & FOUNDATION
|
||||
=========================================
|
||||
|
||||
### STUDENT GOALS:
|
||||
- Dima: Implement prize money system and game logic in app.py
|
||||
- Danil: Create first 5 Russian culture questions
|
||||
- Inna: Make basic question loading and display work
|
||||
- Artyom: Start Russian color theme in CSS
|
||||
|
||||
### TEACHER TASKS:
|
||||
|
||||
✅ Setup Verification:
|
||||
1. Ensure main repo is ready:
|
||||
https://gitea.techshare.cc/technolyceum/ai6-m2
|
||||
|
||||
2. Verify all students have:
|
||||
- Forked the repository
|
||||
- Created their role-specific branch
|
||||
- Made first commit
|
||||
|
||||
3. Quick Progress Check (Terminal):
|
||||
```bash
|
||||
# Check each student's fork manually in Gitea web interface
|
||||
# Look for 4 branches:
|
||||
# - dima-backend-work
|
||||
# - inna-frontend-work
|
||||
# - danil-database-work
|
||||
# - artyom-graphics-work
|
||||
```
|
||||
|
||||
✅ Individual Progress Checks:
|
||||
```bash
|
||||
# Temporary check for each student:
|
||||
git clone [STUDENT-FORK-URL] temp-check
|
||||
cd temp-check
|
||||
git checkout [THEIR-BRANCH]
|
||||
|
||||
# Dima Check:
|
||||
grep -A 15 "PRIZE_LEVELS" app.py
|
||||
|
||||
# Danil Check:
|
||||
python3 -c "import json; print('Questions:', len(json.load(open('questions.json'))))"
|
||||
|
||||
# Inna Check:
|
||||
grep -c "function" static/script.js
|
||||
|
||||
# Artyom Check:
|
||||
grep -c "background" static/style.css
|
||||
|
||||
cd ..
|
||||
rm -rf temp-check
|
||||
```
|
||||
|
||||
=========================================
|
||||
|
||||
## LESSON 3-4: CORE DEVELOPMENT
|
||||
=========================================
|
||||
|
||||
### STUDENT GOALS:
|
||||
- Dima: Complete game session management and answer validation
|
||||
- Danil: 10+ questions completed
|
||||
- Inna: Answer handling system and lifelines
|
||||
- Artyom: Enhanced question/option styling
|
||||
|
||||
### TEACHER TASKS:
|
||||
|
||||
✅ Code Review Workshop:
|
||||
Show examples of DRY (Don't Repeat Yourself) principles:
|
||||
|
||||
**Before (repetitive code):**
|
||||
```javascript
|
||||
document.getElementById('q-number').textContent = data.question_number;
|
||||
document.getElementById('question-text').textContent = data.question;
|
||||
document.getElementById('prize').textContent = data.current_prize;
|
||||
```
|
||||
|
||||
**After (DRY code with utility functions):**
|
||||
```javascript
|
||||
updateElementText('q-number', data.question_number);
|
||||
updateElementText('question-text', data.question);
|
||||
updateElementText('prize', data.current_prize);
|
||||
```
|
||||
|
||||
✅ Git Workshop:
|
||||
```bash
|
||||
# Help students resolve merge conflicts:
|
||||
git fetch origin
|
||||
git checkout main
|
||||
git pull origin main
|
||||
git checkout [THEIR-BRANCH]
|
||||
git merge main
|
||||
|
||||
# If conflicts occur:
|
||||
# 1. Edit conflicted files
|
||||
# 2. Remove conflict markers
|
||||
# 3. git add .
|
||||
# 4. git commit
|
||||
```
|
||||
|
||||
=========================================
|
||||
|
||||
## LESSON 5-6: INTEGRATION & TESTING
|
||||
=========================================
|
||||
|
||||
### STUDENT GOALS:
|
||||
- Dima: Finalize lifelines and error handling
|
||||
- Danil: Complete 15 questions with difficulty progression
|
||||
- Inna: Complete game flow and end game screens
|
||||
- Artyom: Polish all visual elements and animations
|
||||
|
||||
### FINAL INTEGRATION:
|
||||
|
||||
✅ Integration Steps:
|
||||
1. Merge all branches to main:
|
||||
```bash
|
||||
# On main branch:
|
||||
git merge dima-backend-work
|
||||
git merge danil-database-work
|
||||
git merge inna-frontend-work
|
||||
git merge artyom-graphics-work
|
||||
```
|
||||
|
||||
2. Resolve any conflicts
|
||||
|
||||
3. Test complete game flow:
|
||||
- Start game from index.html
|
||||
- Play through all questions
|
||||
- Test lifelines
|
||||
- Verify scoring system
|
||||
- Check end game states
|
||||
|
||||
✅ Final Testing Checklist:
|
||||
- [ ] All 15 questions load correctly
|
||||
- [ ] Answer validation works
|
||||
- [ ] Scoring system functions properly
|
||||
- [ ] Lifelines function correctly
|
||||
- [ ] Game over states work (win/lose)
|
||||
- [ ] Restart functionality works
|
||||
- [ ] Responsive design on different screens
|
||||
- [ ] No console errors in browser
|
||||
|
||||
=========================================
|
||||
|
||||
## PROJECT ARCHITECTURE OVERVIEW
|
||||
=========================================
|
||||
|
||||
### File Structure:
|
||||
```
|
||||
/ai6-m2
|
||||
├── app.py # Flask backend (Dima)
|
||||
├── questions.json # Question database (Danil)
|
||||
├── static/
|
||||
│ ├── script.js # Frontend logic (Inna)
|
||||
│ └── style.css # Styling (Artyom)
|
||||
├── templates/
|
||||
│ ├── index.html # Landing page
|
||||
│ └── game.html # Game interface
|
||||
├── docs/
|
||||
│ ├── roles/ # Role-specific instructions
|
||||
│ └── technical-specs/ # Technical documentation
|
||||
└── README.md # Project overview
|
||||
```
|
||||
|
||||
### Technical Implementation:
|
||||
|
||||
**Backend (app.py):**
|
||||
- Flask routes for game operations
|
||||
- Session management for game state
|
||||
- JSON data handling for questions
|
||||
- Helper functions for DRY code
|
||||
|
||||
**Frontend (script.js):**
|
||||
- API communication with backend
|
||||
- DOM manipulation for game interface
|
||||
- Utility functions for common operations
|
||||
- Event handling for user interactions
|
||||
|
||||
**Database (questions.json):**
|
||||
- JSON format for easy parsing
|
||||
- Structured question data with options
|
||||
- Correct answer validation
|
||||
|
||||
**Styling (style.css):**
|
||||
- Responsive design principles
|
||||
- Russian-themed color scheme
|
||||
- Component-based styling approach
|
||||
|
||||
=========================================
|
||||
|
||||
## TROUBLESHOOTING GUIDE
|
||||
=========================================
|
||||
|
||||
### Common Issues and Solutions:
|
||||
|
||||
1. **"TypeError: Cannot read properties of undefined"**
|
||||
- Usually caused by API returning error instead of data
|
||||
- Solution: Check backend implementation and JSON formatting
|
||||
|
||||
2. **Lifelines not working**
|
||||
- Check session management for lifeline state
|
||||
- Verify frontend-backend communication
|
||||
|
||||
3. **Scoring issues**
|
||||
- Verify prize structure in backend
|
||||
- Check guaranteed level calculations
|
||||
|
||||
4. **Git merge conflicts**
|
||||
- Use systematic approach to resolve conflicts
|
||||
- Communicate with team members about changes
|
||||
|
||||
### Verification Commands:
|
||||
|
||||
```bash
|
||||
# Run the application
|
||||
export FLASK_APP=app.py
|
||||
python -m flask run
|
||||
|
||||
# Check for syntax errors
|
||||
python -m py_compile app.py
|
||||
npx eslint static/script.js
|
||||
|
||||
# Validate JSON
|
||||
python -m json.tool questions.json
|
||||
```
|
||||
|
||||
=========================================
|
||||
|
||||
## ASSESSMENT CRITERIA
|
||||
=========================================
|
||||
|
||||
### Technical Skills:
|
||||
- Code quality and organization (20%)
|
||||
- Implementation of requirements (30%)
|
||||
- Problem-solving and debugging (20%)
|
||||
- Git usage and collaboration (15%)
|
||||
- Documentation and comments (15%)
|
||||
|
||||
### Collaboration:
|
||||
- Team communication
|
||||
- Code review participation
|
||||
- Helpfulness to teammates
|
||||
- Integration contribution
|
||||
|
||||
### Final Product:
|
||||
- Game functions without errors
|
||||
- All features implemented
|
||||
- Visually appealing interface
|
||||
- Good user experience
|
||||
|
||||
🎉 Congratulations on completing the Russian Millionaire Quiz Game project!
|
||||
36
quiz_game/gitea passwords.rtf
Normal file
@@ -0,0 +1,36 @@
|
||||
{\rtf1\ansi\ansicpg1252\cocoartf2818
|
||||
\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fnil\fcharset0 .SFNS-Regular_wdth_opsz110000_GRAD_wght2580000;}
|
||||
{\colortbl;\red255\green255\blue255;\red19\green21\blue25;\red255\green255\blue255;}
|
||||
{\*\expandedcolortbl;;\cssrgb\c9412\c10980\c12941;\cssrgb\c100000\c100000\c100000;}
|
||||
\paperw11900\paperh16840\margl1440\margr1440\vieww11900\viewh14080\viewkind1
|
||||
\deftab720
|
||||
\pard\pardeftab720\partightenfactor0
|
||||
|
||||
\f0\b\fs32 \cf2 \cb3 \expnd0\expndtw0\kerning0
|
||||
Inna\
|
||||
ai6s4\
|
||||
ai6s4@ict.ru\
|
||||
\pard\pardeftab720\partightenfactor0
|
||||
\cf2 Passwords: Student123!\
|
||||
\
|
||||
\cf2 \
|
||||
\pard\pardeftab720\partightenfactor0
|
||||
\cf2 Daniel\
|
||||
ai6s3\
|
||||
ai6s3@ict.ru\
|
||||
\pard\pardeftab720\partightenfactor0
|
||||
\cf2 Passwords: Student123!\cf2 \
|
||||
\pard\pardeftab720\partightenfactor0
|
||||
\cf2 \
|
||||
\
|
||||
Artyom\
|
||||
ai6s2\
|
||||
ai6s2@ict.ru\
|
||||
\pard\pardeftab720\partightenfactor0
|
||||
\cf2 Passwords: Student123!\cf2 \
|
||||
\pard\pardeftab720\partightenfactor0
|
||||
\cf2 \
|
||||
Dima\
|
||||
ai6s1\
|
||||
ai6s1@ict.ru\
|
||||
Passwords: Student123!}
|
||||
27
quiz_game/questions.json
Normal file
@@ -0,0 +1,27 @@
|
||||
[
|
||||
{
|
||||
"question": "What is the capital of Russia?",
|
||||
"options": ["St. Petersburg", "Moscow", "Kazan", "Sochi"],
|
||||
"correct_answer": "Moscow"
|
||||
},
|
||||
{
|
||||
"question": "Which Russian author wrote 'War and Peace'?",
|
||||
"options": ["Dostoevsky", "Tolstoy", "Pushkin", "Chekhov"],
|
||||
"correct_answer": "Tolstoy"
|
||||
},
|
||||
{
|
||||
"question": "What is the traditional Russian soup made with beets?",
|
||||
"options": ["Shchi", "Borscht", "Solyanka", "Ukha"],
|
||||
"correct_answer": "Borscht"
|
||||
},
|
||||
{
|
||||
"question": "Which Russian ruler was known as 'The Terrible'?",
|
||||
"options": ["Peter I", "Catherine II", "Ivan IV", "Nicholas II"],
|
||||
"correct_answer": "Ivan IV"
|
||||
},
|
||||
{
|
||||
"question": "What is the name of the famous Russian ballet company?",
|
||||
"options": ["Moscow Ballet", "St. Petersburg Ballet", "Bolshoi Ballet", "Russian National Ballet"],
|
||||
"correct_answer": "Bolshoi Ballet"
|
||||
}
|
||||
]
|
||||
2
quiz_game/requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
# requirements.txt
|
||||
Flask==2.3.3
|
||||
5
quiz_game/starter_templates/app.py
Normal file
@@ -0,0 +1,5 @@
|
||||
// 1. This file should contain the main application logic
|
||||
// TODO: Implement Flask app setup
|
||||
// TODO: Connect to MongoDB database
|
||||
// TODO: Implement game state management
|
||||
// TODO: Create API endpoints for game functionality
|
||||
5
quiz_game/starter_templates/docs/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
// 1. This file should contain project structure documentation
|
||||
// TODO: Describe directory structure
|
||||
// TODO: Document file naming conventions
|
||||
// TODO: Specify documentation standards
|
||||
// TODO: Add contribution guidelines
|
||||
@@ -0,0 +1,55 @@
|
||||
// 1. This file should contain backend API documentation
|
||||
// TODO: Import Flask and PyMongo (line 1-2)
|
||||
from flask import Flask, session, redirect, url_for, request, jsonify
|
||||
from flask_pymongo import PyMongo
|
||||
|
||||
// TODO: Connect to MongoDB database (line 4-6)
|
||||
app = Flask(__name__)
|
||||
app.config['MONGO_URI'] = 'mongodb://localhost:27017/student_db'
|
||||
mongo = PyMongo(app)
|
||||
|
||||
// TODO: Define API endpoint for getting questions (line 8-10)
|
||||
@app.route('/get_question')
|
||||
def get_question():
|
||||
question_num = session.get('current_question', 0)
|
||||
question = mongo.db.questions.find_one({"question_number": question_num})
|
||||
|
||||
if question:
|
||||
return jsonify({
|
||||
"question": question['question'],
|
||||
"options": question['options'],
|
||||
"question_number": question_num,
|
||||
"current_prize": calculate_prize(question_num)
|
||||
})
|
||||
else:
|
||||
return jsonify({"game_over": True, "final_score": calculate_final_score()})
|
||||
|
||||
// TODO: Define API endpoint for submitting answers (line 12-14)
|
||||
@app.route('/answer', methods=['POST'])
|
||||
def answer_question():
|
||||
user_answer = request.json.get('answer')
|
||||
question_num = session.get('current_question', 0)
|
||||
current_question = mongo.db.questions.find_one({"question_number": question_num})
|
||||
|
||||
is_correct = current_question['options'][current_question['correct_answer']] == user_answer
|
||||
|
||||
if is_correct:
|
||||
session['current_question'] = question_num + 1
|
||||
return jsonify({
|
||||
"correct": True,
|
||||
"correct_answer": current_question['correct_answer'],
|
||||
"game_over": question_num + 1 >= TOTAL_QUESTIONS
|
||||
})
|
||||
else:
|
||||
return jsonify({
|
||||
"correct": False,
|
||||
"correct_answer": current_question['correct_answer'],
|
||||
"game_over": True
|
||||
})
|
||||
|
||||
// TODO: Add error handling for database connection (line 16-18)
|
||||
try:
|
||||
mongo.db.command("ping")
|
||||
print("Successfully connected to MongoDB!")
|
||||
except Exception as e:
|
||||
print(f"MongoDB connection error: {e}")
|
||||
@@ -0,0 +1,45 @@
|
||||
// 1. This file should contain database connection information
|
||||
// TODO: Import MongoDB driver (line 1)
|
||||
import pymongo
|
||||
|
||||
// TODO: Connect to MongoDB using the connection URI (line 3-5)
|
||||
client = pymongo.MongoClient("mongodb://localhost:27017/")
|
||||
|
||||
// TODO: Select the appropriate database (line 7)
|
||||
db = client["student_db"]
|
||||
|
||||
// TODO: Select the questions collection (line 9)
|
||||
questions_collection = db["questions"]
|
||||
|
||||
// TODO: Add error handling for database connection (line 11)
|
||||
try:
|
||||
client.admin.command('ping')
|
||||
print("Successfully connected to MongoDB!")
|
||||
except Exception as e:
|
||||
print(f"MongoDB connection error: {e}")
|
||||
|
||||
// 2. Database population instructions (new section)
|
||||
// TODO: Create function to insert questions into database (line 13-25)
|
||||
def insert_questions():
|
||||
questions = [
|
||||
{
|
||||
"question_number": 0,
|
||||
"question": "What is the capital of Russia?",
|
||||
"options": ["Moscow", "Saint Petersburg", "Novosibirsk", "Yekaterinburg"],
|
||||
"correct_answer": 0
|
||||
},
|
||||
{
|
||||
"question_number": 1,
|
||||
"question": "Which river is the longest in Russia?",
|
||||
"options": ["Volga", "Yenisey", "Ob", "Amur"],
|
||||
"correct_answer": 1
|
||||
}
|
||||
]
|
||||
|
||||
# Insert questions into collection
|
||||
db.questions.insert_many(questions)
|
||||
print("Questions inserted successfully!")
|
||||
|
||||
// TODO: Call insert_questions function (line 27)
|
||||
if __name__ == "__main__"::
|
||||
insert_questions()
|
||||
@@ -0,0 +1,46 @@
|
||||
// 1. This file should contain frontend implementation instructions
|
||||
|
||||
// TODO: Create HTML structure with game container (line 1-5)
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Russian Quiz</title>
|
||||
</head>
|
||||
|
||||
// TODO: Add question display elements (line 7-10)
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Russian Quiz</h1>
|
||||
<div class="score">Prize: <span id="prize">0</span> ₽</div>
|
||||
|
||||
<div class="question-box">
|
||||
<div class="question-number">Question <span id="q-number">1</span>/5</div>
|
||||
<div class="question" id="question-text">Loading...</div>
|
||||
|
||||
<div class="options" id="options">
|
||||
<!-- Options go here -->
|
||||
</div>
|
||||
|
||||
<div class="result" id="result"></div>
|
||||
</div>
|
||||
|
||||
// TODO: Create answer option buttons (line 12-15)
|
||||
<div class="options" id="options">
|
||||
<!-- Options will be dynamically inserted here -->
|
||||
</div>
|
||||
|
||||
<button class="lifeline" onclick="useFiftyFifty()">50:50 Lifeline</button>
|
||||
|
||||
// TODO: Implement game controls and lifelines (line 17-20)
|
||||
<div class="game-over" id="game-over" style="display: none;">
|
||||
<h2>Game Over!</h2>
|
||||
<p>You won: <span id="final-prize">0</span> ₽</p>
|
||||
<button onclick="restartGame()">Play Again</button>
|
||||
</div>
|
||||
|
||||
<script src="{{ url_for('static', filename='script.js') }}"></script>
|
||||
</body>
|
||||
|
||||
// TODO: Add game over screen UI (line 22-25)
|
||||
// Game over screen is already included above as part of the main structure
|
||||
5
quiz_game/starter_templates/static/script.js
Normal file
@@ -0,0 +1,5 @@
|
||||
// 1. This file should contain the game logic
|
||||
// TODO: Implement question loading from API
|
||||
// TODO: Implement answer handling
|
||||
// TODO: Implement lifeline functionality
|
||||
// TODO: Add event listeners for game controls
|
||||
6
quiz_game/starter_templates/static/style.css
Normal file
@@ -0,0 +1,6 @@
|
||||
/* 1. This file should contain basic styling for the game
|
||||
* TODO: Add styles for game container
|
||||
* TODO: Style question display elements
|
||||
* TODO: Style answer options
|
||||
* TODO: Add styles for game controls and lifelines
|
||||
*/
|
||||
5
quiz_game/starter_templates/templates/game.html
Normal file
@@ -0,0 +1,5 @@
|
||||
// 1. This file should contain game HTML structure
|
||||
// TODO: Add game container
|
||||
// TODO: Add question display elements
|
||||
// TODO: Add answer options container
|
||||
// TODO: Add game controls and lifelines buttons
|
||||
5
quiz_game/starter_templates/templates/index.html
Normal file
@@ -0,0 +1,5 @@
|
||||
// 1. This file should contain the main page structure
|
||||
// TODO: Add welcome message
|
||||
// TODO: Add game start button
|
||||
// TODO: Add instructions section
|
||||
// TODO: Add footer with credits
|
||||
149
quiz_game/static/script.js
Normal file
@@ -0,0 +1,149 @@
|
||||
// script.js
|
||||
let currentQuestion = null;
|
||||
|
||||
// Utility function for making API requests
|
||||
function apiRequest(url, options = {}) {
|
||||
return fetch(url, options)
|
||||
.then(response => response.json())
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
|
||||
// Utility function for updating element text content
|
||||
function updateElementText(id, text) {
|
||||
const element = document.getElementById(id);
|
||||
if (element) {
|
||||
element.textContent = text;
|
||||
}
|
||||
}
|
||||
|
||||
// Utility function for showing/hiding elements
|
||||
function toggleElementVisibility(id, show = true) {
|
||||
const element = document.getElementById(id);
|
||||
if (element) {
|
||||
element.style.display = show ? 'block' : 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function loadQuestion() {
|
||||
apiRequest('/get_question')
|
||||
.then(data => {
|
||||
if (data.game_over) {
|
||||
endGame(data.final_score);
|
||||
return;
|
||||
}
|
||||
|
||||
currentQuestion = data;
|
||||
displayQuestion(data);
|
||||
})
|
||||
.catch(error => console.error('Error:', error));
|
||||
}
|
||||
|
||||
function displayQuestion(data) {
|
||||
updateElementText('q-number', data.question_number);
|
||||
updateElementText('question-text', data.question);
|
||||
updateElementText('prize', data.current_prize);
|
||||
|
||||
const optionsContainer = document.getElementById('options');
|
||||
optionsContainer.innerHTML = '';
|
||||
|
||||
data.options.forEach((option, index) => {
|
||||
const optionElement = document.createElement('div');
|
||||
optionElement.className = 'option';
|
||||
optionElement.textContent = option;
|
||||
optionElement.onclick = () => selectAnswer(option);
|
||||
optionsContainer.appendChild(optionElement);
|
||||
});
|
||||
|
||||
// Reset result display
|
||||
toggleElementVisibility('result', false);
|
||||
const result = document.getElementById('result');
|
||||
if (result) {
|
||||
result.className = 'result';
|
||||
}
|
||||
}
|
||||
|
||||
function selectAnswer(answer) {
|
||||
apiRequest('/answer', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({ answer: answer })
|
||||
})
|
||||
.then(data => {
|
||||
const result = document.getElementById('result');
|
||||
if (result) {
|
||||
result.style.display = 'block';
|
||||
|
||||
if (data.correct) {
|
||||
result.textContent = 'Correct!';
|
||||
result.className = 'result correct';
|
||||
|
||||
setTimeout(() => {
|
||||
if (data.game_over) {
|
||||
endGame(data.final_score);
|
||||
} else {
|
||||
loadQuestion();
|
||||
}
|
||||
}, 1500);
|
||||
} else {
|
||||
result.textContent = `Wrong! Correct answer: ${data.correct_answer}`;
|
||||
result.className = 'result incorrect';
|
||||
|
||||
setTimeout(() => {
|
||||
endGame(data.final_score);
|
||||
}, 2000);
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(error => console.error('Error:', error));
|
||||
}
|
||||
|
||||
function useFiftyFifty() {
|
||||
const lifelineBtn = document.querySelector('.lifeline');
|
||||
if (lifelineBtn) {
|
||||
lifelineBtn.disabled = true;
|
||||
}
|
||||
|
||||
apiRequest('/lifeline/fifty_fifty')
|
||||
.then(data => {
|
||||
if (data.error) {
|
||||
alert(data.error);
|
||||
if (lifelineBtn) {
|
||||
lifelineBtn.disabled = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Hide two wrong options
|
||||
const options = document.querySelectorAll('.option');
|
||||
data.remove_indices.forEach(index => {
|
||||
if (options[index]) {
|
||||
options[index].style.display = 'none';
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
if (lifelineBtn) {
|
||||
lifelineBtn.disabled = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function endGame(score) {
|
||||
updateElementText('final-prize', score);
|
||||
toggleElementVisibility('game-over', true);
|
||||
toggleElementVisibility('question-box', false);
|
||||
toggleElementVisibility('lifeline', false);
|
||||
}
|
||||
|
||||
function restartGame() {
|
||||
window.location.href = '/start';
|
||||
}
|
||||
|
||||
// Start the game when page loads
|
||||
if (window.location.pathname === '/start') {
|
||||
loadQuestion();
|
||||
}
|
||||
109
quiz_game/static/style.css
Normal file
@@ -0,0 +1,109 @@
|
||||
/* style.css */
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
background: #f0f0f0;
|
||||
}
|
||||
|
||||
.container {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #1a237e;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.score {
|
||||
text-align: center;
|
||||
font-size: 1.2em;
|
||||
margin: 10px 0;
|
||||
color: #d32f2f;
|
||||
}
|
||||
|
||||
.question-box {
|
||||
background: #e3f2fd;
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
.question-number {
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.question {
|
||||
font-size: 1.1em;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.options {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.option {
|
||||
background: #2196f3;
|
||||
color: white;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.option:hover {
|
||||
background: #1976d2;
|
||||
}
|
||||
|
||||
.option.correct {
|
||||
background: #4caf50;
|
||||
}
|
||||
|
||||
.option.wrong {
|
||||
background: #f44336;
|
||||
}
|
||||
|
||||
.result {
|
||||
margin-top: 10px;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
text-align: center;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.result.correct {
|
||||
background: #c8e6c9;
|
||||
color: #2e7d32;
|
||||
}
|
||||
|
||||
.result.incorrect {
|
||||
background: #ffcdd2;
|
||||
color: #c62828;
|
||||
}
|
||||
|
||||
.start-btn, .lifeline {
|
||||
background: #ff9800;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
margin: 10px auto;
|
||||
}
|
||||
|
||||
.lifeline:disabled {
|
||||
background: #ccc;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.game-over {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
}
|
||||
37
quiz_game/templates/game.html
Normal file
@@ -0,0 +1,37 @@
|
||||
<!-- game.html -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Russian Quiz - Game</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Russian Quiz</h1>
|
||||
<div class="score">Prize: <span id="prize">0</span> ₽</div>
|
||||
|
||||
<div class="question-box">
|
||||
<div class="question-number">Question <span id="q-number">1</span>/5</div>
|
||||
<div class="question" id="question-text">Loading...</div>
|
||||
|
||||
<div class="options" id="options">
|
||||
<!-- Options go here -->
|
||||
</div>
|
||||
|
||||
<div class="result" id="result"></div>
|
||||
</div>
|
||||
|
||||
<button class="lifeline" onclick="useFiftyFifty()">50:50 Lifeline</button>
|
||||
|
||||
<div class="game-over" id="game-over" style="display: none;">
|
||||
<h2>Game Over!</h2>
|
||||
<p>You won: <span id="final-prize">0</span> ₽</p>
|
||||
<button onclick="restartGame()">Play Again</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="{{ url_for('static', filename='script.js') }}"></script>
|
||||
</body>
|
||||
</html>
|
||||
23
quiz_game/templates/index.html
Normal file
@@ -0,0 +1,23 @@
|
||||
<!-- index.html -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Russian Quiz Game</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Russian Quiz</h1>
|
||||
<p>Answer questions about Russian culture!</p>
|
||||
<button onclick="startGame()" class="start-btn">Start Game</button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function startGame() {
|
||||
window.location.href = '/start';
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||