mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-12 07:40:50 -08:00
add elm for Awari
This commit is contained in:
208
00_Alternate_Languages/04_Awari/elm/.gitignore
vendored
Normal file
208
00_Alternate_Languages/04_Awari/elm/.gitignore
vendored
Normal file
@@ -0,0 +1,208 @@
|
||||
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/macos,visualstudiocode,elm
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=macos,visualstudiocode,elm
|
||||
|
||||
### Elm ###
|
||||
# elm-package generated files
|
||||
elm-stuff
|
||||
# elm-repl generated files
|
||||
repl-temp-*
|
||||
|
||||
### macOS ###
|
||||
# General
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
### VisualStudioCode ###
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
!.vscode/*.code-snippets
|
||||
|
||||
# Local History for Visual Studio Code
|
||||
.history/
|
||||
|
||||
# Built Visual Studio Code Extensions
|
||||
*.vsix
|
||||
|
||||
### VisualStudioCode Patch ###
|
||||
# Ignore all local history of files
|
||||
.history
|
||||
.ionide
|
||||
|
||||
# Support for Project snippet scope
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/macos,visualstudiocode,elm
|
||||
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/node
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=node
|
||||
|
||||
### Node ###
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
.temp
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
.docusaurus
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
||||
### Node Patch ###
|
||||
# Serverless Webpack directories
|
||||
.webpack/
|
||||
|
||||
# Optional stylelint cache
|
||||
|
||||
# SvelteKit build / generate output
|
||||
.svelte-kit
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/node
|
||||
|
||||
app.js
|
||||
12
00_Alternate_Languages/04_Awari/elm/README.md
Normal file
12
00_Alternate_Languages/04_Awari/elm/README.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# Awari
|
||||
|
||||
This is an Elm implementation of the `Basic Compouter Games` Game Awari.
|
||||
|
||||
## Build App
|
||||
|
||||
- install elm
|
||||
|
||||
```bash
|
||||
yarn
|
||||
yarn build
|
||||
```
|
||||
19
00_Alternate_Languages/04_Awari/elm/docs/index.html
Normal file
19
00_Alternate_Languages/04_Awari/elm/docs/index.html
Normal file
@@ -0,0 +1,19 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<link rel="stylesheet" href="https://unpkg.com/@picocss/pico@latest/css/pico.min.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="elm-app-is-loaded-here" />
|
||||
<script src="app.js"></script>
|
||||
<script>
|
||||
var app = Elm.Main.init({
|
||||
node: document.getElementById("elm-app-is-loaded-here"),
|
||||
flags: {}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
24
00_Alternate_Languages/04_Awari/elm/elm.json
Normal file
24
00_Alternate_Languages/04_Awari/elm/elm.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"type": "application",
|
||||
"source-directories": [
|
||||
"src"
|
||||
],
|
||||
"elm-version": "0.19.1",
|
||||
"dependencies": {
|
||||
"direct": {
|
||||
"elm/browser": "1.0.2",
|
||||
"elm/core": "1.0.5",
|
||||
"elm/html": "1.0.0"
|
||||
},
|
||||
"indirect": {
|
||||
"elm/json": "1.1.3",
|
||||
"elm/time": "1.0.0",
|
||||
"elm/url": "1.0.0",
|
||||
"elm/virtual-dom": "1.0.2"
|
||||
}
|
||||
},
|
||||
"test-dependencies": {
|
||||
"direct": {},
|
||||
"indirect": {}
|
||||
}
|
||||
}
|
||||
13
00_Alternate_Languages/04_Awari/elm/package.json
Normal file
13
00_Alternate_Languages/04_Awari/elm/package.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "04_Awari",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"live": "elm-live src/Main.elm --proxy-prefix=/api --proxy-host=http://localhost:8080/api --open --start-page=resources/index.html -- --output=app.js --debug",
|
||||
"build": "elm make src/Main.elm --optimize --output docs/app.js && cp -R resources/ docs/"
|
||||
},
|
||||
"devDependencies": {
|
||||
"elm-live": "^4.0.2"
|
||||
}
|
||||
}
|
||||
19
00_Alternate_Languages/04_Awari/elm/resources/index.html
Normal file
19
00_Alternate_Languages/04_Awari/elm/resources/index.html
Normal file
@@ -0,0 +1,19 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<link rel="stylesheet" href="https://unpkg.com/@picocss/pico@latest/css/pico.min.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="elm-app-is-loaded-here" />
|
||||
<script src="app.js"></script>
|
||||
<script>
|
||||
var app = Elm.Main.init({
|
||||
node: document.getElementById("elm-app-is-loaded-here"),
|
||||
flags: {}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
424
00_Alternate_Languages/04_Awari/elm/src/Main.elm
Normal file
424
00_Alternate_Languages/04_Awari/elm/src/Main.elm
Normal file
@@ -0,0 +1,424 @@
|
||||
module Main exposing (main)
|
||||
|
||||
import Array exposing (Array, repeat)
|
||||
import Browser
|
||||
import Html exposing (..)
|
||||
import Html.Attributes exposing (style)
|
||||
import Html.Events exposing (onClick)
|
||||
|
||||
|
||||
|
||||
-- Main
|
||||
|
||||
|
||||
main : Program () Model Msg
|
||||
main =
|
||||
Browser.element
|
||||
{ init = init
|
||||
, view = view
|
||||
, update = update
|
||||
, subscriptions = \_ -> Sub.none
|
||||
}
|
||||
|
||||
|
||||
|
||||
-- Model
|
||||
|
||||
|
||||
type alias Model =
|
||||
{ board : Array Int
|
||||
, playerOnesTurn : Bool
|
||||
, gameFinished : Bool
|
||||
}
|
||||
|
||||
|
||||
init : () -> ( Model, Cmd Msg )
|
||||
init _ =
|
||||
( { board = initArray 0 36 (repeat boardLength 0), playerOnesTurn = False, gameFinished = False }, Cmd.none )
|
||||
|
||||
|
||||
|
||||
-- Messages
|
||||
|
||||
|
||||
type Msg
|
||||
= BoardClicked Int
|
||||
| Reset
|
||||
|
||||
|
||||
|
||||
-- Update
|
||||
|
||||
|
||||
update : Msg -> Model -> ( Model, Cmd Msg )
|
||||
update msg model =
|
||||
case msg of
|
||||
BoardClicked index ->
|
||||
if indexIsOnPit index || index < rightPitIndex && model.playerOnesTurn || index > rightPitIndex && not model.playerOnesTurn then
|
||||
( model, Cmd.none )
|
||||
|
||||
else
|
||||
let
|
||||
lastStoneInPitIndex =
|
||||
lastStonePitPosition index model.board
|
||||
|
||||
boardAfterStonesSet =
|
||||
changesOnBoardAfterStonesSet model.playerOnesTurn lastStoneInPitIndex (boardClicked index model.board)
|
||||
|
||||
nextPlayer =
|
||||
determineNextPlayer lastStoneInPitIndex model.playerOnesTurn boardAfterStonesSet
|
||||
in
|
||||
( { model | board = boardAfterStonesSet, playerOnesTurn = nextPlayer, gameFinished = calculateGameFinished boardAfterStonesSet }, Cmd.none )
|
||||
|
||||
Reset ->
|
||||
init ()
|
||||
|
||||
|
||||
|
||||
-- Constants
|
||||
|
||||
|
||||
boardLength : Int
|
||||
boardLength =
|
||||
14
|
||||
|
||||
|
||||
leftPitIndex : Int
|
||||
leftPitIndex =
|
||||
0
|
||||
|
||||
|
||||
rightPitIndex : Int
|
||||
rightPitIndex =
|
||||
7
|
||||
|
||||
|
||||
|
||||
-- View
|
||||
|
||||
|
||||
view : Model -> Html Msg
|
||||
view model =
|
||||
div [ style "margin" "4rem" ]
|
||||
[ h1 [] [ text "Awari" ]
|
||||
, viewDescription
|
||||
, getPlayerTurnText False model.playerOnesTurn
|
||||
, viewBoard model
|
||||
, viewWinnerIfGameFinished model
|
||||
, getPlayerTurnText True model.playerOnesTurn
|
||||
, div
|
||||
[ style "display" "flex"
|
||||
, style "justify-content" "center"
|
||||
]
|
||||
[ button [ onClick Reset, style "max-width" "10rem", style "margin" "0", style "display" "inline-block" ] [ text "Restart" ] ]
|
||||
]
|
||||
|
||||
|
||||
getPlayerTurnText : Bool -> Bool -> Html msg
|
||||
getPlayerTurnText playerOne playerOnesTurn =
|
||||
if playerOne && playerOnesTurn || not playerOnesTurn && not playerOne then
|
||||
div [ style "height" "2rem", style "margin" "1rem 0", style "font-size" "1.5rem" ]
|
||||
[ text
|
||||
("It's your turn Player "
|
||||
++ (if playerOnesTurn then
|
||||
" 2 "
|
||||
|
||||
else
|
||||
" 1 "
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
else
|
||||
div [ style "height" "2rem", style "margin" "1rem", style "font-size" "1.5rem" ] []
|
||||
|
||||
|
||||
viewWinnerIfGameFinished : Model -> Html msg
|
||||
viewWinnerIfGameFinished model =
|
||||
if not model.gameFinished then
|
||||
div [] []
|
||||
|
||||
else
|
||||
div [] [ text ("Winner is" ++ winnerPlayer model) ]
|
||||
|
||||
|
||||
winnerPlayer : Model -> String
|
||||
winnerPlayer model =
|
||||
if getLeftPitValue model.board > getRightPitValue model.board then
|
||||
"Player 1"
|
||||
|
||||
else if getLeftPitValue model.board < getRightPitValue model.board then
|
||||
"Player 2"
|
||||
|
||||
else
|
||||
"None"
|
||||
|
||||
|
||||
viewBoard : Model -> Html Msg
|
||||
viewBoard model =
|
||||
let
|
||||
boardGenerator =
|
||||
boardCard model.playerOnesTurn
|
||||
in
|
||||
div boardSyle
|
||||
(Array.toList <|
|
||||
Array.indexedMap boardGenerator model.board
|
||||
)
|
||||
|
||||
|
||||
boardSyle : List (Attribute msg)
|
||||
boardSyle =
|
||||
[ style "display" "flex"
|
||||
, style "gap" "1rem"
|
||||
, style "display" "grid"
|
||||
, style "grid-template-columns" "repeat(8, 1fr)"
|
||||
, style "align-items" "center"
|
||||
, style "grid-template-areas" """
|
||||
'a b c d e f g h'
|
||||
'a n m l k j i h'
|
||||
"""
|
||||
]
|
||||
|
||||
|
||||
boardCard : Bool -> Int -> Int -> Html Msg
|
||||
boardCard playerOnesTurn index element =
|
||||
div (boardCardStyle playerOnesTurn index) [ text (String.fromInt element) ]
|
||||
|
||||
|
||||
boardCardStyle : Bool -> Int -> List (Attribute Msg)
|
||||
boardCardStyle playerOnesTurn index =
|
||||
if not playerOnesTurn && index < rightPitIndex || playerOnesTurn && index >= rightPitIndex then
|
||||
[ onClick (BoardClicked index)
|
||||
, style "grid-area" (areaFromIndex index)
|
||||
, style "cursor" "pointer"
|
||||
, style "background" "#36454F"
|
||||
, style "border-radius" "6px"
|
||||
, style "text-align" "center"
|
||||
, style "padding" "0.5rem"
|
||||
]
|
||||
|
||||
else
|
||||
[ style "grid-area" (areaFromIndex index)
|
||||
, style "background" "black"
|
||||
, style "border-radius" "6px"
|
||||
, style "text-align" "center"
|
||||
, style "padding" "0.5rem"
|
||||
]
|
||||
|
||||
|
||||
viewDescription : Html msg
|
||||
viewDescription =
|
||||
details []
|
||||
[ summary [] [ text "How the game works" ]
|
||||
, p [] [ text """
|
||||
Awari is an ancient African game played with seven sticks and thirty-six stones or beans laid out as shown above. The board is divided into six compartments or pits on each side. In addition, there are two special home pits at the ends.
|
||||
|
||||
A move is made by taking all the beans from any (non-empty) pit on your own side. Starting from the pit to the right of this one, these beans are ‘sown’ one in each pit working around the board anticlockwise.
|
||||
|
||||
A turn consists of one or two moves. If the last bean of your move is sown in your own home you may take a second move.
|
||||
|
||||
If the last bean sown in a move lands in an empty pit, provided that the opposite pit is not empty, all the beans in the opposite pit, together with the last bean sown are ‘captured’ and moved to the player’s home.
|
||||
|
||||
When either side is empty, the game is finished. The player with the most beans in his home has won.
|
||||
""" ]
|
||||
]
|
||||
|
||||
|
||||
|
||||
-- Functions
|
||||
|
||||
|
||||
checkIfPlayerCanNotDoMove : Bool -> Array Int -> Bool
|
||||
checkIfPlayerCanNotDoMove playerOnesTurn board =
|
||||
if playerOnesTurn then
|
||||
Array.foldr (+) 0 (Array.slice (rightPitIndex + 1) boardLength board) == 0
|
||||
|
||||
else
|
||||
Array.foldr (+) 0 (Array.slice (leftPitIndex + 1) rightPitIndex board) == 0
|
||||
|
||||
|
||||
determineNextPlayer : Int -> Bool -> Array Int -> Bool
|
||||
determineNextPlayer index playerOnesTurn board =
|
||||
if checkIfPlayerCanNotDoMove (not playerOnesTurn) board then
|
||||
playerOnesTurn
|
||||
|
||||
else if indexIsOnPit index then
|
||||
playerOnesTurn
|
||||
|
||||
else
|
||||
not playerOnesTurn
|
||||
|
||||
|
||||
indexIsOnPit : Int -> Bool
|
||||
indexIsOnPit index =
|
||||
index == leftPitIndex || index == rightPitIndex
|
||||
|
||||
|
||||
calculateGameFinished : Array Int -> Bool
|
||||
calculateGameFinished board =
|
||||
Array.foldr (+) 0 board - getLeftPitValue board - getRightPitValue board == 0
|
||||
|
||||
|
||||
getLeftPitValue : Array Int -> Int
|
||||
getLeftPitValue board =
|
||||
getPitValue leftPitIndex board
|
||||
|
||||
|
||||
getRightPitValue : Array Int -> Int
|
||||
getRightPitValue board =
|
||||
getPitValue rightPitIndex board
|
||||
|
||||
|
||||
getPitValue : Int -> Array Int -> Int
|
||||
getPitValue index board =
|
||||
Maybe.withDefault 0 (Array.get index board)
|
||||
|
||||
|
||||
lastStonePitPosition : Int -> Array Int -> Int
|
||||
lastStonePitPosition index board =
|
||||
calcLastStonePosition index board (Maybe.withDefault 0 (Array.get index board))
|
||||
|
||||
|
||||
calcLastStonePosition : Int -> Array Int -> Int -> Int
|
||||
calcLastStonePosition index board stones =
|
||||
if stones == 0 then
|
||||
index
|
||||
|
||||
else
|
||||
calcLastStonePosition (incrementRotatingIndex index board) board (stones - 1)
|
||||
|
||||
|
||||
changesOnBoardAfterStonesSet : Bool -> Int -> Array Int -> Array Int
|
||||
changesOnBoardAfterStonesSet playerOne lastStonesIndex board =
|
||||
if Maybe.withDefault 0 (Array.get lastStonesIndex board) == 1 then
|
||||
if indexIsOnPit lastStonesIndex then
|
||||
board
|
||||
|
||||
else
|
||||
let
|
||||
sumStones =
|
||||
Maybe.withDefault 0 (Array.get (boardLength - lastStonesIndex) board) + 1
|
||||
|
||||
currentPit =
|
||||
if playerOne then
|
||||
getRightPitValue board
|
||||
|
||||
else
|
||||
getLeftPitValue board
|
||||
|
||||
baordWithIndexZero =
|
||||
Array.set lastStonesIndex 0 board
|
||||
|
||||
baordWithOppositeZero =
|
||||
Array.set (boardLength - lastStonesIndex) 0 baordWithIndexZero
|
||||
|
||||
boardWithPitSum =
|
||||
if playerOne then
|
||||
Array.set rightPitIndex (sumStones + currentPit) baordWithOppositeZero
|
||||
|
||||
else
|
||||
Array.set leftPitIndex (sumStones + currentPit) baordWithOppositeZero
|
||||
in
|
||||
boardWithPitSum
|
||||
|
||||
else
|
||||
board
|
||||
|
||||
|
||||
initArray : Int -> Int -> Array Int -> Array Int
|
||||
initArray index stones array =
|
||||
if stones == 0 then
|
||||
array
|
||||
|
||||
else if indexIsOnPit index then
|
||||
initArray (index + 1) stones array
|
||||
|
||||
else
|
||||
initArray (index + 1) (stones - 3) (Array.set index 3 array)
|
||||
|
||||
|
||||
boardClicked : Int -> Array Int -> Array Int
|
||||
boardClicked index board =
|
||||
if indexIsOnPit index then
|
||||
board
|
||||
|
||||
else
|
||||
let
|
||||
stonesLeft =
|
||||
Maybe.withDefault 0 (Array.get index board)
|
||||
|
||||
startBoard =
|
||||
Array.set index 0 board
|
||||
in
|
||||
placeStones (incrementRotatingIndex index startBoard) stonesLeft startBoard
|
||||
|
||||
|
||||
incrementRotatingIndex : Int -> Array Int -> Int
|
||||
incrementRotatingIndex currentIndex board =
|
||||
if 0 == currentIndex then
|
||||
Array.length board - 1
|
||||
|
||||
else
|
||||
currentIndex - 1
|
||||
|
||||
|
||||
placeStones : Int -> Int -> Array Int -> Array Int
|
||||
placeStones index stonesLeft board =
|
||||
if stonesLeft == 0 then
|
||||
board
|
||||
|
||||
else
|
||||
let
|
||||
oldStones =
|
||||
Maybe.withDefault 0 (Array.get index board)
|
||||
in
|
||||
placeStones (incrementRotatingIndex index board) (stonesLeft - 1) (Array.set index (oldStones + 1) board)
|
||||
|
||||
|
||||
areaFromIndex : Int -> String
|
||||
areaFromIndex index =
|
||||
case index of
|
||||
0 ->
|
||||
"a"
|
||||
|
||||
1 ->
|
||||
"b"
|
||||
|
||||
2 ->
|
||||
"c"
|
||||
|
||||
3 ->
|
||||
"d"
|
||||
|
||||
4 ->
|
||||
"e"
|
||||
|
||||
5 ->
|
||||
"f"
|
||||
|
||||
6 ->
|
||||
"g"
|
||||
|
||||
7 ->
|
||||
"h"
|
||||
|
||||
8 ->
|
||||
"i"
|
||||
|
||||
9 ->
|
||||
"j"
|
||||
|
||||
10 ->
|
||||
"k"
|
||||
|
||||
11 ->
|
||||
"l"
|
||||
|
||||
12 ->
|
||||
"m"
|
||||
|
||||
13 ->
|
||||
"n"
|
||||
|
||||
i ->
|
||||
String.fromInt i
|
||||
Reference in New Issue
Block a user