Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 03bcb465cf | |||
| b2721c2ec3 | |||
| 1385b3ace0 | |||
| 66a028880c | |||
| 365b12b0eb | |||
| 5ef6747e4d |
@@ -30,8 +30,104 @@ jobs:
|
||||
- name: Build APK
|
||||
run: ./gradlew assembleRelease
|
||||
|
||||
- name: Upload APK
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: app-release
|
||||
path: app/build/outputs/apk/release/app-release.apk
|
||||
- name: Locate APK
|
||||
id: locate_apk
|
||||
run: |
|
||||
set -e
|
||||
APK_PATH=$(ls app/build/outputs/apk/release/*.apk | head -n1 || true)
|
||||
if [ -z "$APK_PATH" ]; then
|
||||
echo "No APK found in app/build/outputs/apk/release" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "APK_PATH=$APK_PATH" >> $GITHUB_ENV
|
||||
echo "Found $APK_PATH"
|
||||
|
||||
# Note: uploading artifacts with actions/upload-artifact@v4 is not supported
|
||||
# on some self-hosted/enterprise runners (GHES). We skip storing artifacts
|
||||
# via that action and instead upload the APK directly to the Gitea release
|
||||
# in the steps below.
|
||||
|
||||
- name: Create Gitea release
|
||||
id: create_release
|
||||
env:
|
||||
GITEA_TOKEN: ${{ secrets.GIT_TOKEN }}
|
||||
GITEA_SERVER: ${{ secrets.GIT_SERVER }}
|
||||
run: |
|
||||
set -e
|
||||
SERVER_CLEAN=$(echo "$GITEA_SERVER" | sed 's#/$##')
|
||||
API_BASE="${SERVER_CLEAN}/api/v1"
|
||||
|
||||
OWNER="${{ github.repository_owner }}"
|
||||
REPO_NAME=$(echo "${{ github.repository }}" | cut -d/ -f2)
|
||||
TAG="${{ github.ref_name }}"
|
||||
|
||||
echo "Debug Info:"
|
||||
echo "- API URL: $API_BASE/repos/$OWNER/$REPO_NAME/releases"
|
||||
echo "- OWNER: $OWNER"
|
||||
echo "- REPO: $REPO_NAME"
|
||||
echo "- TAG: $TAG"
|
||||
|
||||
# Próba utworzenia release z przechwyceniem statusu HTTP
|
||||
RESPONSE_FILE=$(mktemp)
|
||||
HTTP_STATUS=$(curl -s -o "$RESPONSE_FILE" -w "%{http_code}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: token $GITEA_TOKEN" \
|
||||
-X POST \
|
||||
-d "{
|
||||
\"tag_name\": \"$TAG\",
|
||||
\"name\": \"$TAG\",
|
||||
\"body\": \"Automated release for $TAG\",
|
||||
\"draft\": false,
|
||||
\"prerelease\": false
|
||||
}" \
|
||||
"$API_BASE/repos/$OWNER/$REPO_NAME/releases")
|
||||
|
||||
RESPONSE_BODY=$(cat "$RESPONSE_FILE")
|
||||
echo "HTTP Status: $HTTP_STATUS"
|
||||
echo "Response: $RESPONSE_BODY"
|
||||
|
||||
if [ "$HTTP_STATUS" -eq 409 ]; then
|
||||
echo "Release already exists, fetching existing ID..."
|
||||
RESPONSE_BODY=$(curl -s -H "Authorization: token $GITEA_TOKEN" "$API_BASE/repos/$OWNER/$REPO_NAME/releases/tags/$TAG")
|
||||
RELEASE_ID=$(echo "$RESPONSE_BODY" | sed -n 's/.*"id":\([0-9]*\),.*/\1/p' | head -n1)
|
||||
elif [ "$HTTP_STATUS" -eq 201 ]; then
|
||||
RELEASE_ID=$(echo "$RESPONSE_BODY" | sed -n 's/.*"id":\([0-9]*\),.*/\1/p' | head -n1)
|
||||
else
|
||||
echo "Failed to create release. Expected 201 or 409, got $HTTP_STATUS"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$RELEASE_ID" ]; then
|
||||
echo "Failed to extract RELEASE_ID from response"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "RELEASE_ID=$RELEASE_ID" >> $GITHUB_ENV
|
||||
echo "Successfully processed release ID: $RELEASE_ID"
|
||||
|
||||
- name: Upload APK to Gitea release
|
||||
env:
|
||||
GITEA_TOKEN: ${{ secrets.GIT_TOKEN }}
|
||||
GITEA_SERVER: ${{ secrets.GIT_SERVER }}
|
||||
run: |
|
||||
set -e
|
||||
SERVER_CLEAN=$(echo "$GITEA_SERVER" | sed 's#/$##')
|
||||
API_BASE="${SERVER_CLEAN}/api/v1"
|
||||
|
||||
OWNER="${{ github.repository_owner }}"
|
||||
REPO_NAME=$(echo "${{ github.repository }}" | cut -d/ -f2)
|
||||
|
||||
if [ -z "$APK_PATH" ]; then
|
||||
echo "APK_PATH not set" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "$RELEASE_ID" ]; then
|
||||
echo "RELEASE_ID not set" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
UPLOAD_URL="$API_BASE/repos/$OWNER/$REPO_NAME/releases/$RELEASE_ID/assets?name=$(basename $APK_PATH)"
|
||||
echo "Uploading $APK_PATH to $UPLOAD_URL"
|
||||
|
||||
curl --fail -H "Authorization: token $GITEA_TOKEN" -H "Content-Type: application/octet-stream" \
|
||||
--data-binary @"$APK_PATH" "$UPLOAD_URL"
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
name: Release APK on Tag (Gitea Actions)
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
# Note: This workflow requires a repository secret named GITEA_TOKEN with a personal access token
|
||||
# that has `repo` (or appropriate) scope for creating releases and uploading assets.
|
||||
# Optionally set GITEA_SERVER to your Gitea server base URL (e.g. https://gitea.example.com).
|
||||
|
||||
jobs:
|
||||
build-and-release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Ensure JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: '17'
|
||||
|
||||
- name: Build release APK
|
||||
run: |
|
||||
set -e
|
||||
./gradlew :app:assembleRelease --no-daemon --stacktrace
|
||||
|
||||
- name: Locate APK
|
||||
id: locate_apk
|
||||
run: |
|
||||
set -e
|
||||
APK_PATH=$(ls app/build/outputs/apk/release/*.apk | head -n1 || true)
|
||||
if [ -z "$APK_PATH" ]; then
|
||||
echo "No APK found in app/build/outputs/apk/release"
|
||||
exit 1
|
||||
fi
|
||||
echo "APK_PATH=$APK_PATH" >> $GITHUB_ENV
|
||||
echo "Found $APK_PATH"
|
||||
|
||||
- name: Create Gitea release
|
||||
id: create_release
|
||||
env:
|
||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||
GITEA_SERVER: ${{ secrets.GITEA_SERVER }}
|
||||
run: |
|
||||
set -e
|
||||
# Determine API base URL
|
||||
if [ -n "$GITEA_SERVER" ]; then
|
||||
API_BASE="$GITEA_SERVER/api/v1"
|
||||
else
|
||||
API_BASE="https://gitea.com/api/v1"
|
||||
fi
|
||||
|
||||
# Derive owner/repo from git remote
|
||||
REMOTE_URL=$(git config --get remote.origin.url || true)
|
||||
if [ -z "$REMOTE_URL" ]; then
|
||||
echo "Cannot determine remote origin URL" >&2
|
||||
exit 1
|
||||
fi
|
||||
REPO_FULL=$(echo "$REMOTE_URL" | sed -E 's#.*[:/](.+/.+)\.git$#\1#')
|
||||
OWNER=$(echo "$REPO_FULL" | cut -d/ -f1)
|
||||
REPO_NAME=$(echo "$REPO_FULL" | cut -d/ -f2)
|
||||
|
||||
TAG=${GITHUB_REF_NAME:-$(echo $GITHUB_REF | sed 's#refs/tags/##')}
|
||||
if [ -z "$TAG" ]; then
|
||||
# fallback: use git to get tag from HEAD
|
||||
TAG=$(git describe --tags --exact-match 2>/dev/null || true)
|
||||
fi
|
||||
if [ -z "$TAG" ]; then
|
||||
echo "Cannot determine tag name" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Creating release $TAG for $OWNER/$REPO_NAME against $API_BASE"
|
||||
|
||||
CREATE_RESPONSE=$(curl -s -H "Content-Type: application/json" -H "Authorization: token $GITEA_TOKEN" \
|
||||
-d "{\"tag_name\": \"$TAG\", \"name\": \"$TAG\", \"body\": \"Automated release for $TAG\", \"draft\": false, \"prerelease\": false}" \
|
||||
"$API_BASE/repos/$OWNER/$REPO_NAME/releases")
|
||||
|
||||
RELEASE_ID=$(echo "$CREATE_RESPONSE" | grep -o '"id":[0-9]*' | head -n1 | cut -d: -f2 | tr -d ' ')
|
||||
if [ -z "$RELEASE_ID" ]; then
|
||||
echo "Failed to create release. Response: $CREATE_RESPONSE" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "RELEASE_ID=$RELEASE_ID" >> $GITHUB_ENV
|
||||
echo "Created release id $RELEASE_ID"
|
||||
|
||||
- name: Upload APK to Gitea release
|
||||
env:
|
||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||
GITEA_SERVER: ${{ secrets.GITEA_SERVER }}
|
||||
run: |
|
||||
set -e
|
||||
API_BASE=${GITEA_SERVER:+$GITEA_SERVER/api/v1}
|
||||
API_BASE=${API_BASE:-https://gitea.com/api/v1}
|
||||
REMOTE_URL=$(git config --get remote.origin.url || true)
|
||||
REPO_FULL=$(echo "$REMOTE_URL" | sed -E 's#.*[:/](.+/.+)\.git$#\1#')
|
||||
OWNER=$(echo "$REPO_FULL" | cut -d/ -f1)
|
||||
REPO_NAME=$(echo "$REPO_FULL" | cut -d/ -f2)
|
||||
APK_PATH=${APK_PATH:-$APK_PATH}
|
||||
if [ -z "$APK_PATH" ]; then
|
||||
echo "APK_PATH not set" >&2
|
||||
exit 1
|
||||
fi
|
||||
RELEASE_ID=${RELEASE_ID}
|
||||
if [ -z "$RELEASE_ID" ]; then
|
||||
echo "RELEASE_ID not set" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
UPLOAD_URL="$API_BASE/repos/$OWNER/$REPO_NAME/releases/$RELEASE_ID/assets?name=$(basename $APK_PATH)"
|
||||
echo "Uploading $APK_PATH to $UPLOAD_URL"
|
||||
|
||||
curl --fail -H "Authorization: token $GITEA_TOKEN" -H "Content-Type: application/octet-stream" \
|
||||
--data-binary @"$APK_PATH" "$UPLOAD_URL"
|
||||
@@ -1,68 +0,0 @@
|
||||
name: Release APK on Tag
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
build-and-release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '17'
|
||||
|
||||
- name: Cache Gradle
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-gradle-
|
||||
|
||||
- name: Build release APK
|
||||
run: ./gradlew :app:assembleRelease --no-daemon --stacktrace
|
||||
|
||||
- name: Find APK
|
||||
id: find_apk
|
||||
run: |
|
||||
set -e
|
||||
APK=$(ls app/build/outputs/apk/release/*.apk | head -n1 || true)
|
||||
if [ -z "$APK" ]; then
|
||||
echo "No APK found in app/build/outputs/apk/release"
|
||||
exit 1
|
||||
fi
|
||||
echo "APK_PATH=$APK" >> $GITHUB_ENV
|
||||
echo "apk=$APK"
|
||||
|
||||
- name: Upload artifact (for debugging)
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: app-release-apk
|
||||
path: ${{ env.APK_PATH }}
|
||||
|
||||
- name: Create GitHub Release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
with:
|
||||
tag_name: ${{ github.ref_name }}
|
||||
release_name: Release ${{ github.ref_name }}
|
||||
body: Automated release for tag ${{ github.ref_name }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
|
||||
- name: Upload APK to Release
|
||||
uses: actions/upload-release-asset@v1
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ${{ env.APK_PATH }}
|
||||
asset_name: app-release.apk
|
||||
asset_content_type: application/vnd.android.package-archive
|
||||
+36
-1
@@ -1,3 +1,38 @@
|
||||
.DS_Store
|
||||
./QuizzyTemplate
|
||||
./QuizzyTemplate/*
|
||||
/.QuizzyTemplate
|
||||
|
||||
# Android/Gradle
|
||||
/.gradle/
|
||||
/build/
|
||||
/app/build/
|
||||
/**/build/
|
||||
|
||||
# Gradle wrapper
|
||||
/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Local configuration
|
||||
/local.properties
|
||||
|
||||
# Keystore
|
||||
*.jks
|
||||
|
||||
# Generated APKs/outputs
|
||||
**/outputs/
|
||||
**/apk/**
|
||||
*.apk
|
||||
*.ap_
|
||||
|
||||
# IntelliJ / Android Studio
|
||||
.idea/
|
||||
*.iml
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
|
||||
# NPM / Node
|
||||
node_modules/
|
||||
|
||||
# Misc
|
||||
*.log
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Generated
+26
-13
@@ -13,6 +13,18 @@
|
||||
<component name="ClangdSettings">
|
||||
<option name="formatViaClangd" value="false" />
|
||||
</component>
|
||||
<component name="ExternalProjectsManager">
|
||||
<system id="GRADLE">
|
||||
<state>
|
||||
<projects_view>
|
||||
<tree_state>
|
||||
<expand />
|
||||
<select />
|
||||
</tree_state>
|
||||
</projects_view>
|
||||
</state>
|
||||
</system>
|
||||
</component>
|
||||
<component name="Git.Settings">
|
||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
||||
</component>
|
||||
@@ -25,20 +37,21 @@
|
||||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</component>
|
||||
<component name="PropertiesComponent">{
|
||||
"keyToString": {
|
||||
"ModuleVcsDetector.initialDetectionPerformed": "true",
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"RunOnceActivity.cidr.known.project.marker": "true",
|
||||
"RunOnceActivity.git.unshallow": "true",
|
||||
"RunOnceActivity.readMode.enableVisualFormatting": "true",
|
||||
"cf.first.check.clang-format": "false",
|
||||
"cidr.known.project.marker": "true",
|
||||
"git-widget-placeholder": "master",
|
||||
"kotlin-language-version-configured": "true",
|
||||
"last_opened_file_path": "/Users/aln/Work/Matma"
|
||||
<component name="PropertiesComponent"><![CDATA[{
|
||||
"keyToString": {
|
||||
"ModuleVcsDetector.initialDetectionPerformed": "true",
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"RunOnceActivity.cidr.known.project.marker": "true",
|
||||
"RunOnceActivity.git.unshallow": "true",
|
||||
"RunOnceActivity.readMode.enableVisualFormatting": "true",
|
||||
"android.gradle.sync.needed": "true",
|
||||
"cf.first.check.clang-format": "false",
|
||||
"cidr.known.project.marker": "true",
|
||||
"git-widget-placeholder": "master",
|
||||
"kotlin-language-version-configured": "true",
|
||||
"last_opened_file_path": "/Users/aln/Work/Matma"
|
||||
}
|
||||
}</component>
|
||||
}]]></component>
|
||||
<component name="TaskManager">
|
||||
<task active="true" id="Default" summary="Default task">
|
||||
<changelist id="51538617-7e5b-4e71-9f47-7bda274cf4cc" name="Changes" comment="" />
|
||||
|
||||
@@ -9,6 +9,7 @@ const state = {
|
||||
timeLeft: 60,
|
||||
sessionSolved: 0,
|
||||
sessionTarget: 20,
|
||||
history: [],
|
||||
settings: {
|
||||
timedSeconds: 60,
|
||||
maxResult: 40,
|
||||
@@ -137,6 +138,7 @@ function startPlay(){
|
||||
menuScreen.classList.add('hidden')
|
||||
playScreen.classList.remove('hidden')
|
||||
state.score = 0
|
||||
state.history = []
|
||||
scoreEl.textContent = state.score
|
||||
state.answerBuffer = ''
|
||||
answerEl.textContent = ''
|
||||
@@ -160,53 +162,74 @@ function startPlay(){
|
||||
|
||||
function generateProblem(){
|
||||
const ops = Array.from(state.ops)
|
||||
const op = ops[Math.floor(Math.random()*ops.length)]
|
||||
const maxOp = Math.max(1, state.settings.maxOperand)
|
||||
const maxRes = Math.max(1, state.settings.maxResult)
|
||||
let a = 0, b = 0
|
||||
for (let i=0;i<200;i++){
|
||||
let a = 0, b = 0, op = '';
|
||||
|
||||
const maxAttempts = 200;
|
||||
for (let i = 0; i < maxAttempts; i++) {
|
||||
op = ops[Math.floor(Math.random()*ops.length)]
|
||||
let candidate = null;
|
||||
|
||||
if (op === 'div'){
|
||||
b = randInt(1, maxOp)
|
||||
if (!state.settings.allowFraction){
|
||||
const maxQByOp = Math.floor(maxOp / b)
|
||||
const maxQ = Math.min(maxRes, maxQByOp)
|
||||
if (maxQ < 0) continue
|
||||
const q = randInt(0, maxQ)
|
||||
a = q * b
|
||||
if (state.settings.allowNegative && Math.random() < 0.2) a = -a
|
||||
return {a,b,op}
|
||||
if (maxQ >= 0) {
|
||||
const q = randInt(0, maxQ)
|
||||
a = q * b
|
||||
if (state.settings.allowNegative && Math.random() < 0.2) a = -a
|
||||
candidate = {a,b,op}
|
||||
}
|
||||
} else {
|
||||
const maxResultNByOp = Math.floor((10 * maxOp) / b)
|
||||
const maxResultN = Math.min(maxRes * 10, maxResultNByOp)
|
||||
if (maxResultN < 0) continue
|
||||
const result_n = randInt(0, maxResultN)
|
||||
if ((result_n * b) % 10 !== 0) continue
|
||||
a = (result_n * b) / 10
|
||||
if (state.settings.allowNegative && Math.random() < 0.2) a = -a
|
||||
return {a,b,op}
|
||||
if (maxResultN >= 0) {
|
||||
const result_n = randInt(0, maxResultN)
|
||||
if ((result_n * b) % 10 === 0) {
|
||||
a = (result_n * b) / 10
|
||||
if (state.settings.allowNegative && Math.random() < 0.2) a = -a
|
||||
candidate = {a,b,op}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (op === 'mul'){
|
||||
a = randInt(0, maxOp)
|
||||
b = randInt(0, maxOp)
|
||||
if (a * b <= maxRes) return {a,b,op}
|
||||
if (a * b <= maxRes) candidate = {a,b,op}
|
||||
} else if (op === 'add'){
|
||||
a = randInt(0, maxOp)
|
||||
b = randInt(0, maxOp)
|
||||
if (a + b <= maxRes) return {a,b,op}
|
||||
if (a + b <= maxRes) candidate = {a,b,op}
|
||||
} else if (op === 'sub'){
|
||||
a = randInt(0, maxOp)
|
||||
b = randInt(0, maxOp)
|
||||
if (!state.settings.allowNegative){
|
||||
if (a < b) [a,b] = [b,a]
|
||||
if (a - b <= maxRes) return {a,b,op}
|
||||
if (a - b <= maxRes) candidate = {a,b,op}
|
||||
} else {
|
||||
if (Math.abs(a - b) <= maxRes) return {a,b,op}
|
||||
if (Math.abs(a - b) <= maxRes) candidate = {a,b,op}
|
||||
}
|
||||
}
|
||||
|
||||
if (candidate) {
|
||||
// Generujemy klucz dla historii. Dla dodawania i mnożenia 2+3 to to samo co 3+2.
|
||||
const key = (op === 'add' || op === 'mul')
|
||||
? `${op}:${[candidate.a, candidate.b].sort().join(',')}`
|
||||
: `${op}:${candidate.a},${candidate.b}`;
|
||||
|
||||
if (!state.history.includes(key) || i === maxAttempts - 1) {
|
||||
state.history.push(key);
|
||||
if (state.history.length > 20) state.history.shift();
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
a = Math.min(maxOp, Math.floor(maxRes/2))
|
||||
b = Math.min(maxOp, 1)
|
||||
return {a,b,op}
|
||||
return {a,b,op: ops[0] || 'add'}
|
||||
}
|
||||
|
||||
function randInt(min,max){
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Nauka Dzielenia
|
||||
;(function () {
|
||||
const st = { divisor: null, total: 20, solved: 0, score: 0, current: null, buf: '' }
|
||||
const st = { divisor: null, total: 20, solved: 0, score: 0, current: null, buf: '', history: [] }
|
||||
|
||||
const selectScreen = document.getElementById('select-screen')
|
||||
const playScreen = document.getElementById('play-screen')
|
||||
@@ -26,13 +26,14 @@
|
||||
st.total = Math.max(5, Math.min(100, parseInt(totalInput.value, 10) || 20))
|
||||
st.solved = 0
|
||||
st.score = 0
|
||||
st.history = []
|
||||
show(playScreen)
|
||||
nextProblem()
|
||||
})
|
||||
|
||||
document.getElementById('back-btn').addEventListener('click', () => show(selectScreen))
|
||||
document.getElementById('again-btn').addEventListener('click', () => {
|
||||
st.solved = 0; st.score = 0; show(playScreen); nextProblem()
|
||||
st.solved = 0; st.score = 0; st.history = []; show(playScreen); nextProblem()
|
||||
})
|
||||
document.getElementById('change-btn').addEventListener('click', () => show(selectScreen))
|
||||
|
||||
@@ -60,9 +61,28 @@
|
||||
function randInt(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min }
|
||||
|
||||
function nextProblem() {
|
||||
const b = st.divisor || randInt(1, 10) // divisor
|
||||
const answer = randInt(1, 10) // quotient (always integer)
|
||||
const a = b * answer // dividend
|
||||
let a, b, answer, key;
|
||||
const maxAttempts = 50;
|
||||
|
||||
for (let i = 0; i < maxAttempts; i++) {
|
||||
b = st.divisor || randInt(1, 10) // divisor
|
||||
answer = randInt(1, 10) // quotient
|
||||
a = b * answer // dividend
|
||||
|
||||
// Jeśli wybrano konkretny dzielnik, kluczem jest iloraz (wynik).
|
||||
// W przeciwnym razie kluczem jest całe działanie.
|
||||
key = st.divisor ? answer : `${a}:${b}`;
|
||||
|
||||
if (!st.history.includes(key) || i === maxAttempts - 1) {
|
||||
st.history.push(key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (st.history.length > (st.divisor ? 6 : 15)) {
|
||||
st.history.shift();
|
||||
}
|
||||
|
||||
st.current = { a, b, answer }
|
||||
problemEl.textContent = `${a} ÷ ${b}`
|
||||
feedbackEl.textContent = ''
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Nauka Mnożenia
|
||||
;(function () {
|
||||
const st = { table: null, total: 20, solved: 0, score: 0, current: null, buf: '' }
|
||||
const st = { table: null, total: 20, solved: 0, score: 0, current: null, buf: '', history: [] }
|
||||
|
||||
const selectScreen = document.getElementById('select-screen')
|
||||
const playScreen = document.getElementById('play-screen')
|
||||
@@ -27,13 +27,14 @@
|
||||
st.total = Math.max(5, Math.min(100, parseInt(totalInput.value, 10) || 20))
|
||||
st.solved = 0
|
||||
st.score = 0
|
||||
st.history = []
|
||||
show(playScreen)
|
||||
nextProblem()
|
||||
})
|
||||
|
||||
document.getElementById('back-btn').addEventListener('click', () => show(selectScreen))
|
||||
document.getElementById('again-btn').addEventListener('click', () => {
|
||||
st.solved = 0; st.score = 0; show(playScreen); nextProblem()
|
||||
st.solved = 0; st.score = 0; st.history = []; show(playScreen); nextProblem()
|
||||
})
|
||||
document.getElementById('change-btn').addEventListener('click', () => show(selectScreen))
|
||||
|
||||
@@ -61,8 +62,26 @@
|
||||
function randInt(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min }
|
||||
|
||||
function nextProblem() {
|
||||
const a = st.table || randInt(1, 10)
|
||||
const b = randInt(1, 10)
|
||||
let a, b, key;
|
||||
const maxAttempts = 50;
|
||||
|
||||
for (let i = 0; i < maxAttempts; i++) {
|
||||
a = st.table || randInt(1, 10)
|
||||
b = randInt(1, 10)
|
||||
// Jeśli wybrano konkretną tabelkę, kluczem jest tylko drugi składnik.
|
||||
// Jeśli "wszystkie", kluczem jest posortowana para (żeby 2x3 i 3x2 były traktowane tak samo).
|
||||
key = st.table ? b : [a, b].sort().join('x');
|
||||
|
||||
if (!st.history.includes(key) || i === maxAttempts - 1) {
|
||||
st.history.push(key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (st.history.length > (st.table ? 6 : 15)) {
|
||||
st.history.shift();
|
||||
}
|
||||
|
||||
st.current = { a, b, answer: a * b }
|
||||
problemEl.textContent = `${a} × ${b}`
|
||||
feedbackEl.textContent = ''
|
||||
|
||||
Reference in New Issue
Block a user