From 1139e086fcf44881a8a9d797e3a4c38fd28d7728 Mon Sep 17 00:00:00 2001 From: slapelachie Date: Fri, 21 Mar 2025 10:51:17 +1000 Subject: [PATCH] init --- .clang-format | 17 + .gitignore | 1 + 100words.txt | 100 +++ Makefile | 13 + a1results.txt | 589 ++++++++++++++++++ checka1.sh | 1 + empty.txt | 0 testa1.sh | 1056 ++++++++++++++++++++++++++++++++ testfiles-custom/1.1.stderr | 1 + testfiles-custom/10.1.stdout | 7 + testfiles-custom/2.1.stderr | 1 + testfiles-custom/2.2.stderr | 1 + testfiles-custom/2.3.stderr | 1 + testfiles-custom/2.4.stderr | 1 + testfiles-custom/2.5.stderr | 1 + testfiles-custom/2.6.stderr | 1 + testfiles-custom/2.7.stderr | 1 + testfiles-custom/2.8.stderr | 1 + testfiles-custom/3.1.stderr | 1 + testfiles-custom/3.1.stdout | 2 + testfiles-custom/3.2.stdout | 2 + testfiles-custom/4.1.stdout | 4 + testfiles-custom/4.3.stderr | 1 + testfiles-custom/4.3.stdout | 14 + testfiles-custom/5.1.stderr | 1 + testfiles-custom/5.1.stdout | 4 + testfiles-custom/5.2.stderr | 1 + testfiles-custom/5.2.stdout | 18 + testfiles-custom/5.3.stderr | 1 + testfiles-custom/5.3.stdout | 10 + testfiles-custom/6.1.stderr | 1 + testfiles-custom/6.1.stdout | 4 + testfiles-custom/6.2.stderr | 1 + testfiles-custom/6.2.stdout | 12 + testfiles-custom/6.3.stderr | 1 + testfiles-custom/6.4.stderr | 1 + testfiles-custom/6.4.stdout | 16 + testfiles-custom/7.1.stderr | 1 + testfiles-custom/7.1.stdout | 4 + testfiles-custom/7.2.stderr | 1 + testfiles-custom/7.2.stdout | 12 + testfiles-custom/7.3.stderr | 1 + testfiles-custom/7.3.stdout | 10 + testfiles-custom/7.4.stderr | 1 + testfiles-custom/7.4.stdout | 12 + testfiles-custom/8.1.stderr | 1 + testfiles-custom/8.1.stdout | 12 + testfiles-custom/8.2.stderr | 1 + testfiles-custom/8.2.stdout | 7 + testfiles-custom/8.3.stderr | 1 + testfiles-custom/8.3.stdout | 12 + testfiles-custom/9.1.stderr | 1 + testfiles-custom/9.1.stdout | 4 + testfiles-custom/9.2.stdout | 6 + testfiles-custom/empty | 0 testfiles-custom/eof.1 | 1 + testfiles-custom/eof.2 | 2 + testfiles-custom/incorrect.1 | 1 + testfiles-custom/incorrect.2 | 5 + testfiles-custom/incorrect.3 | 4 + testfiles-custom/incorrect.4 | 5 + testfiles-custom/invalid.1 | 1 + testfiles-custom/invalid.2 | 5 + testfiles-custom/invalid.3 | 5 + testfiles-custom/invalid.4 | 7 + testfiles-custom/nonletters.1 | 1 + testfiles-custom/nonletters.2 | 8 + testfiles-custom/nonletters.3 | 4 + testfiles-custom/right.1 | 3 + testfiles-custom/right.2 | 5 + testfiles-custom/run_wordle.sh | 37 ++ testfiles-custom/wronglen.1 | 1 + testfiles-custom/wronglen.2 | 1 + testfiles-custom/wronglen.3 | 6 + wordle.c | 368 +++++++++++ wordle.h | 270 ++++++++ 76 files changed, 2715 insertions(+) create mode 100644 .clang-format create mode 100644 .gitignore create mode 100644 100words.txt create mode 100644 Makefile create mode 100644 a1results.txt create mode 100644 checka1.sh create mode 100644 empty.txt create mode 100644 testa1.sh create mode 100644 testfiles-custom/1.1.stderr create mode 100644 testfiles-custom/10.1.stdout create mode 100644 testfiles-custom/2.1.stderr create mode 100644 testfiles-custom/2.2.stderr create mode 100644 testfiles-custom/2.3.stderr create mode 100644 testfiles-custom/2.4.stderr create mode 100644 testfiles-custom/2.5.stderr create mode 100644 testfiles-custom/2.6.stderr create mode 100644 testfiles-custom/2.7.stderr create mode 100644 testfiles-custom/2.8.stderr create mode 100644 testfiles-custom/3.1.stderr create mode 100644 testfiles-custom/3.1.stdout create mode 100644 testfiles-custom/3.2.stdout create mode 100644 testfiles-custom/4.1.stdout create mode 100644 testfiles-custom/4.3.stderr create mode 100644 testfiles-custom/4.3.stdout create mode 100644 testfiles-custom/5.1.stderr create mode 100644 testfiles-custom/5.1.stdout create mode 100644 testfiles-custom/5.2.stderr create mode 100644 testfiles-custom/5.2.stdout create mode 100644 testfiles-custom/5.3.stderr create mode 100644 testfiles-custom/5.3.stdout create mode 100644 testfiles-custom/6.1.stderr create mode 100644 testfiles-custom/6.1.stdout create mode 100644 testfiles-custom/6.2.stderr create mode 100644 testfiles-custom/6.2.stdout create mode 100644 testfiles-custom/6.3.stderr create mode 100644 testfiles-custom/6.4.stderr create mode 100644 testfiles-custom/6.4.stdout create mode 100644 testfiles-custom/7.1.stderr create mode 100644 testfiles-custom/7.1.stdout create mode 100644 testfiles-custom/7.2.stderr create mode 100644 testfiles-custom/7.2.stdout create mode 100644 testfiles-custom/7.3.stderr create mode 100644 testfiles-custom/7.3.stdout create mode 100644 testfiles-custom/7.4.stderr create mode 100644 testfiles-custom/7.4.stdout create mode 100644 testfiles-custom/8.1.stderr create mode 100644 testfiles-custom/8.1.stdout create mode 100644 testfiles-custom/8.2.stderr create mode 100644 testfiles-custom/8.2.stdout create mode 100644 testfiles-custom/8.3.stderr create mode 100644 testfiles-custom/8.3.stdout create mode 100644 testfiles-custom/9.1.stderr create mode 100644 testfiles-custom/9.1.stdout create mode 100644 testfiles-custom/9.2.stdout create mode 100644 testfiles-custom/empty create mode 100644 testfiles-custom/eof.1 create mode 100644 testfiles-custom/eof.2 create mode 100644 testfiles-custom/incorrect.1 create mode 100644 testfiles-custom/incorrect.2 create mode 100644 testfiles-custom/incorrect.3 create mode 100644 testfiles-custom/incorrect.4 create mode 100644 testfiles-custom/invalid.1 create mode 100644 testfiles-custom/invalid.2 create mode 100644 testfiles-custom/invalid.3 create mode 100644 testfiles-custom/invalid.4 create mode 100644 testfiles-custom/nonletters.1 create mode 100644 testfiles-custom/nonletters.2 create mode 100644 testfiles-custom/nonletters.3 create mode 100644 testfiles-custom/right.1 create mode 100644 testfiles-custom/right.2 create mode 100644 testfiles-custom/run_wordle.sh create mode 100644 testfiles-custom/wronglen.1 create mode 100644 testfiles-custom/wronglen.2 create mode 100644 testfiles-custom/wronglen.3 create mode 100644 wordle.c create mode 100644 wordle.h diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..da0fa44 --- /dev/null +++ b/.clang-format @@ -0,0 +1,17 @@ +--- +BasedOnStyle: Google +IndentWidth: 4 +ColumnLimit: 79 +IndentWidth: 4 +ContinuationIndentWidth: 8 +UseTab: Never + +PointerAlignment: Right +BreakBeforeBraces: Attach +AlignAfterOpenBracket: DontAlign +AlignTrailingComments: true +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortEnumsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +--- diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dea75a2 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +wordle diff --git a/100words.txt b/100words.txt new file mode 100644 index 0000000..a00b362 --- /dev/null +++ b/100words.txt @@ -0,0 +1,100 @@ +1080 +10-point +10th +11-point +12-point +16-point +18-point +1st +2 +20-point +2,4,5-t +2,4-d +2D +2nd +30-30 +3-D +3-d +3D +3M +3rd +48-point +4-D +4GL +4H +4th +5-point +5-T +5th +6-point +6th +7-point +7th +8-point +8th +9-point +9th +-a +A +A. +a +a' +a- +a. +A-1 +A1 +a1 +A4 +A5 +AA +aa +A.A.A. +AAA +aaa +AAAA +AAAAAA +AAAL +AAAS +Aaberg +Aachen +AAE +AAEE +AAF +AAG +aah +aahed +aahing +aahs +AAII +aal +Aalborg +Aalesund +aalii +aaliis +aals +Aalst +Aalto +AAM +aam +AAMSI +Aandahl +A-and-R +Aani +AAO +AAP +AAPSS +Aaqbiye +Aar +Aara +Aarau +AARC +aardvark +aardvarks +aardwolf +aardwolves +Aaren +Aargau +aargh +Aarhus +Aarika +Aaron diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9888822 --- /dev/null +++ b/Makefile @@ -0,0 +1,13 @@ +CC=gcc +CFLAGS=-std=c99 -Wall -pedantic -I/local/courses/csse2310/include -L/local/courses/csse2310/lib -lcsse2310a1 + +all: wordle + +wordle: wordle.c wordle.h + $(CC) $(CFLAGS) -o $@ $^ + +wordle-debug: wordle.c wordle.h + $(CC) $(CFLAGS) -pg -fprofile-arcs -ftest-coverage -o $@ $^ + +clean: + rm wordle wordle-debug diff --git a/a1results.txt b/a1results.txt new file mode 100644 index 0000000..0b77cf8 --- /dev/null +++ b/a1results.txt @@ -0,0 +1,589 @@ +gcc -std=c99 -Wall -pedantic -I/local/courses/csse2310/include -L/local/courses/csse2310/lib -lcsse2310a1 -o wordle wordle.c wordle.h +gcc -std=c99 -Wall -pedantic -I/local/courses/csse2310/include -L/local/courses/csse2310/lib -lcsse2310a1 -g -o wordle-debug wordle.c wordle.h +.../s4638520/Documents/CSSE2310/assessment1/wordle.h OK +.../s4638520/Documents/CSSE2310/assessment1/wordle.c OK + +THIS IS NOT A GUARANTEE OF CORRECTNESS +THE STYLE GUIDE IS FINAL +Setting up for tests +Time: Tue Mar 22 13:08:55 AEST 2022 +Running make... +make: Nothing to be done for 'all'. +Running tests ... +---------------------------------------------------------------------- +1.1 - invalid_usage01 PASS +1.2 - invalid_usage02 PASS +1.3 - invalid_usage03 PASS +1.4 - invalid_usage04 PASS +1.5 - invalid_usage05 PASS +1.6 - invalid_usage06 PASS +1.7 - invalid_usage07 PASS +1.8 - invalid_usage08 PASS +1.9 - invalid_usage09 PASS +1.10 - invalid_usage10 PASS +1.11 - invalid_usage11 PASS +1.12 - invalid_usage12 PASS +1.13 - invalid_usage13 PASS +1.14 - invalid_usage14 PASS +1.15 - invalid_usage15 PASS +1.16 - invalid_usage16 PASS +1.17 - invalid_usage17 PASS +1.18 - invalid_usage18 PASS +1.19 - invalid_usage19 PASS +1.20 - invalid_usage20 PASS +2.1 - invalid_file01 PASS +2.2 - invalid_file02 PASS +2.3 - invalid_file03 PASS +2.4 - invalid_file04 PASS +2.5 - invalid_file05 PASS +2.6 - invalid_file06 PASS +2.7 - invalid_file07 PASS +2.8 - invalid_file08 PASS +3.1 - prompt01 PASS +3.2 - prompt02 PASS +4.1 - wrong_len01 PASS +4.2 - wrong_len02 PASS +4.3 - wrong_len03 PASS +5.1 - non_letters01 PASS +5.2 - non_letters02 PASS +5.3 - non_letters03 PASS +6.1 - incorrect_guesses01 PASS +6.2 - incorrect_guesses02 PASS +6.3 - incorrect_guesses03 PASS +6.4 - incorrect_guesses04 PASS +7.1 - various_capitalisation01 PASS +7.2 - various_capitalisation02 PASS +7.3 - various_capitalisation03 PASS +7.4 - various_capitalisation04 PASS +8.1 - running_out_of_attempts01 PASS +8.2 - running_out_of_attempts02 PASS +8.3 - running_out_of_attempts03 PASS +9.1 - handles_eof01 PASS +9.2 - handles_eof02 PASS +10.1 - right_answer01 PASS +10.2 - right_answer02 PASS +---------------------------------------------------------------------- +Cleaning up ... +Done +---------------------------------------------------------------------- +=== 1.1 - invalid_usage01 === +Description: Check that args 'one two' considered invalid +What the test runs: + ./wordle 'one' 'two' > 1.1.stdout 2> 1.1.stderr +What the test checks: + Expect exit code: 1 + Standard output (1.1.stdout) should match testfiles/empty + Standard error (1.1.stderr) should match testfiles/1.1.stderr + + +=== 1.2 - invalid_usage02 === +Description: Check that args 'one two three' considered invalid +What the test runs: + ./wordle 'one' 'two' 'three' > 1.2.stdout 2> 1.2.stderr +What the test checks: + Expect exit code: 1 + Standard output (1.2.stdout) should match testfiles/empty + Standard error (1.2.stderr) should match testfiles/1.1.stderr + + +=== 1.3 - invalid_usage03 === +Description: Check that args 'one two three four' considered invalid +What the test runs: + ./wordle 'one' 'two' 'three' 'four' > 1.3.stdout 2> 1.3.stderr +What the test checks: + Expect exit code: 1 + Standard output (1.3.stdout) should match testfiles/empty + Standard error (1.3.stderr) should match testfiles/1.1.stderr + + +=== 1.4 - invalid_usage04 === +Description: Check that args 'one two three four five' considered invalid +What the test runs: + ./wordle 'one' 'two' 'three' 'four' 'five' > 1.4.stdout 2> 1.4.stderr +What the test checks: + Expect exit code: 1 + Standard output (1.4.stdout) should match testfiles/empty + Standard error (1.4.stderr) should match testfiles/1.1.stderr + + +=== 1.5 - invalid_usage05 === +Description: Check that args 'one two three four five six' considered +invalid +What the test runs: + ./wordle 'one' 'two' 'three' 'four' 'five' 'six' > 1.5.stdout 2> 1.5.stderr +What the test checks: + Expect exit code: 1 + Standard output (1.5.stdout) should match testfiles/empty + Standard error (1.5.stderr) should match testfiles/1.1.stderr + + +=== 1.6 - invalid_usage06 === +Description: Check that args '-max' considered invalid +What the test runs: + ./wordle '-max' > 1.6.stdout 2> 1.6.stderr +What the test checks: + Expect exit code: 1 + Standard output (1.6.stdout) should match testfiles/empty + Standard error (1.6.stderr) should match testfiles/1.1.stderr + + +=== 1.7 - invalid_usage07 === +Description: Check that args '-len' considered invalid +What the test runs: + ./wordle '-len' > 1.7.stdout 2> 1.7.stderr +What the test checks: + Expect exit code: 1 + Standard output (1.7.stdout) should match testfiles/empty + Standard error (1.7.stderr) should match testfiles/1.1.stderr + + +=== 1.8 - invalid_usage08 === +Description: Check that args '-max nonnumber' considered invalid +What the test runs: + ./wordle '-max' 'nonnumber' > 1.8.stdout 2> 1.8.stderr +What the test checks: + Expect exit code: 1 + Standard output (1.8.stdout) should match testfiles/empty + Standard error (1.8.stderr) should match testfiles/1.1.stderr + + +=== 1.9 - invalid_usage09 === +Description: Check that args '-len nonnumber' considered invalid +What the test runs: + ./wordle '-len' 'nonnumber' > 1.9.stdout 2> 1.9.stderr +What the test checks: + Expect exit code: 1 + Standard output (1.9.stdout) should match testfiles/empty + Standard error (1.9.stderr) should match testfiles/1.1.stderr + + +=== 1.10 - invalid_usage10 === +Description: Check that args '-max 5 -len' considered invalid +What the test runs: + ./wordle '-max' '5' '-len' > 1.10.stdout 2> 1.10.stderr +What the test checks: + Expect exit code: 1 + Standard output (1.10.stdout) should match testfiles/empty + Standard error (1.10.stderr) should match testfiles/1.1.stderr + + +=== 1.11 - invalid_usage11 === +Description: Check that args '-max 2' considered invalid +What the test runs: + ./wordle '-max' '2' > 1.11.stdout 2> 1.11.stderr +What the test checks: + Expect exit code: 1 + Standard output (1.11.stdout) should match testfiles/empty + Standard error (1.11.stderr) should match testfiles/1.1.stderr + + +=== 1.12 - invalid_usage12 === +Description: Check that args '-max 10' considered invalid +What the test runs: + ./wordle '-max' '10' > 1.12.stdout 2> 1.12.stderr +What the test checks: + Expect exit code: 1 + Standard output (1.12.stdout) should match testfiles/empty + Standard error (1.12.stderr) should match testfiles/1.1.stderr + + +=== 1.13 - invalid_usage13 === +Description: Check that args '-max 3abc' considered invalid +What the test runs: + ./wordle '-max' '3abc' > 1.13.stdout 2> 1.13.stderr +What the test checks: + Expect exit code: 1 + Standard output (1.13.stdout) should match testfiles/empty + Standard error (1.13.stderr) should match testfiles/1.1.stderr + + +=== 1.14 - invalid_usage14 === +Description: Check that args '-max 3 dictionary extra-arg' considered +invalid +What the test runs: + ./wordle '-max' '3' 'dictionary' 'extra-arg' > 1.14.stdout 2> 1.14.stderr +What the test checks: + Expect exit code: 1 + Standard output (1.14.stdout) should match testfiles/empty + Standard error (1.14.stderr) should match testfiles/1.1.stderr + + +=== 1.15 - invalid_usage15 === +Description: Check that args '-max 8 -len nonnumber' considered invalid +What the test runs: + ./wordle '-max' '8' '-len' 'nonnumber' > 1.15.stdout 2> 1.15.stderr +What the test checks: + Expect exit code: 1 + Standard output (1.15.stdout) should match testfiles/empty + Standard error (1.15.stderr) should match testfiles/1.1.stderr + + +=== 1.16 - invalid_usage16 === +Description: Check that args '-max 8 -max 8' considered invalid +What the test runs: + ./wordle '-max' '8' '-max' '8' > 1.16.stdout 2> 1.16.stderr +What the test checks: + Expect exit code: 1 + Standard output (1.16.stdout) should match testfiles/empty + Standard error (1.16.stderr) should match testfiles/1.1.stderr + + +=== 1.17 - invalid_usage17 === +Description: Check that args '-guess' considered invalid +What the test runs: + ./wordle '-guess' > 1.17.stdout 2> 1.17.stderr +What the test checks: + Expect exit code: 1 + Standard output (1.17.stdout) should match testfiles/empty + Standard error (1.17.stderr) should match testfiles/1.1.stderr + + +=== 1.18 - invalid_usage18 === +Description: Check that args '-max 3 -max 5 /usr/share/dict/words' +considered invalid +What the test runs: + ./wordle '-max' '3' '-max' '5' '/usr/share/dict/words' > 1.18.stdout 2> 1.18.stderr +What the test checks: + Expect exit code: 1 + Standard output (1.18.stdout) should match testfiles/empty + Standard error (1.18.stderr) should match testfiles/1.1.stderr + + +=== 1.19 - invalid_usage19 === +Description: Check that args '-max 5 -len 5 /usr/share/dict/words extra' +considered invalid +What the test runs: + ./wordle '-max' '5' '-len' '5' '/usr/share/dict/words' 'extra' > 1.19.stdout 2> 1.19.stderr +What the test checks: + Expect exit code: 1 + Standard output (1.19.stdout) should match testfiles/empty + Standard error (1.19.stderr) should match testfiles/1.1.stderr + + +=== 1.20 - invalid_usage20 === +Description: Check that args '-guess 8' considered invalid +What the test runs: + ./wordle '-guess' '8' > 1.20.stdout 2> 1.20.stderr +What the test checks: + Expect exit code: 1 + Standard output (1.20.stdout) should match testfiles/empty + Standard error (1.20.stderr) should match testfiles/1.1.stderr + + +=== 2.1 - invalid_file01 === +Description: Dictionary 'hello.there.csse2310' unable to be opened with +program args '' +What the test runs: + ./wordle 'hello.there.csse2310' > 2.1.stdout 2> 2.1.stderr +What the test checks: + Expect exit code: 2 + Standard output (2.1.stdout) should match testfiles/empty + Standard error (2.1.stderr) should match testfiles/2.1.stderr + + +=== 2.2 - invalid_file02 === +Description: Dictionary '/does/not/exist' unable to be opened with +program args '' +What the test runs: + ./wordle '/does/not/exist' > 2.2.stdout 2> 2.2.stderr +What the test checks: + Expect exit code: 2 + Standard output (2.2.stdout) should match testfiles/empty + Standard error (2.2.stderr) should match testfiles/2.2.stderr + + +=== 2.3 - invalid_file03 === +Description: Dictionary '/var/log/cron' unable to be opened with program +args '' +What the test runs: + ./wordle '/var/log/cron' > 2.3.stdout 2> 2.3.stderr +What the test checks: + Expect exit code: 2 + Standard output (2.3.stdout) should match testfiles/empty + Standard error (2.3.stderr) should match testfiles/2.3.stderr + + +=== 2.4 - invalid_file04 === +Description: Dictionary '../nope' unable to be opened with program args '' +What the test runs: + ./wordle '../nope' > 2.4.stdout 2> 2.4.stderr +What the test checks: + Expect exit code: 2 + Standard output (2.4.stdout) should match testfiles/empty + Standard error (2.4.stderr) should match testfiles/2.4.stderr + + +=== 2.5 - invalid_file05 === +Description: Dictionary 'hello.there.csse2310' unable to be opened with +program args '-len 4' +What the test runs: + ./wordle '-len' '4' 'hello.there.csse2310' > 2.5.stdout 2> 2.5.stderr +What the test checks: + Expect exit code: 2 + Standard output (2.5.stdout) should match testfiles/empty + Standard error (2.5.stderr) should match testfiles/2.5.stderr + + +=== 2.6 - invalid_file06 === +Description: Dictionary '/does/not/exist' unable to be opened with +program args '-len 4' +What the test runs: + ./wordle '-len' '4' '/does/not/exist' > 2.6.stdout 2> 2.6.stderr +What the test checks: + Expect exit code: 2 + Standard output (2.6.stdout) should match testfiles/empty + Standard error (2.6.stderr) should match testfiles/2.6.stderr + + +=== 2.7 - invalid_file07 === +Description: Dictionary '/var/log/cron' unable to be opened with program +args '-len 4' +What the test runs: + ./wordle '-len' '4' '/var/log/cron' > 2.7.stdout 2> 2.7.stderr +What the test checks: + Expect exit code: 2 + Standard output (2.7.stdout) should match testfiles/empty + Standard error (2.7.stderr) should match testfiles/2.7.stderr + + +=== 2.8 - invalid_file08 === +Description: Dictionary '../nope' unable to be opened with program args +'-len 4' +What the test runs: + ./wordle '-len' '4' '../nope' > 2.8.stdout 2> 2.8.stderr +What the test checks: + Expect exit code: 2 + Standard output (2.8.stdout) should match testfiles/empty + Standard error (2.8.stderr) should match testfiles/2.8.stderr + + +=== 3.1 - prompt01 === +Description: Check for welcome and prompt +What the test runs: + testfiles/run_wordle.sh 'right' '/dev/null' '-lines' '2' > 3.1.stdout 2> 3.1.stderr +What the test checks: + Standard output (3.1.stdout) should match testfiles/3.1.stdout + + +=== 3.2 - prompt02 === +Description: Check for welcome and prompt 2 +What the test runs: + testfiles/run_wordle.sh 'right' '/dev/null' '-lines' '2' '-max' '3' > 3.2.stdout 2> 3.2.stderr +What the test checks: + Standard output (3.2.stdout) should match testfiles/3.2.stdout + + +=== 4.1 - wrong_len01 === +Description: Check guess of length 4 with default word length +What the test runs: + testfiles/run_wordle.sh 'right' 'testfiles/wronglen.1' > 4.1.stdout 2> 4.1.stderr +What the test checks: + Standard output (4.1.stdout) should match testfiles/4.1.stdout + + +=== 4.2 - wrong_len02 === +Description: Check guess of length 0 with default word length +What the test runs: + testfiles/run_wordle.sh 'right' 'testfiles/wronglen.2' > 4.2.stdout 2> 4.2.stderr +What the test checks: + Standard output (4.2.stdout) should match testfiles/4.1.stdout + + +=== 4.3 - wrong_len03 === +Description: Check guesses that are too long (word length 4) +What the test runs: + testfiles/run_wordle.sh 'word' 'testfiles/wronglen.3' '-len' '4' > 4.3.stdout 2> 4.3.stderr +What the test checks: + Standard output (4.3.stdout) should match testfiles/4.3.stdout + + +=== 5.1 - non_letters01 === +Description: Check program handles guesses with non letters - with args +'-len 6 -max 7' +What the test runs: + testfiles/run_wordle.sh 'right' 'testfiles/nonletters.1' > 5.1.stdout 2> 5.1.stderr +What the test checks: + Standard output (5.1.stdout) should match testfiles/5.1.stdout + + +=== 5.2 - non_letters02 === +Description: Check program handles guesses with non letters - with args +'-len 6 -max 7' +What the test runs: + testfiles/run_wordle.sh 'there' 'testfiles/nonletters.2' > 5.2.stdout 2> 5.2.stderr +What the test checks: + Standard output (5.2.stdout) should match testfiles/5.2.stdout + + +=== 5.3 - non_letters03 === +Description: Check program handles guesses with non letters - with args +'-len 6 -max 7' +What the test runs: + testfiles/run_wordle.sh 'cat' 'testfiles/nonletters.3' '-len' '3' > 5.3.stdout 2> 5.3.stderr +What the test checks: + Standard output (5.3.stdout) should match testfiles/5.3.stdout + + +=== 6.1 - incorrect_guesses01 === +Description: Single invalid guess, default dictionary and other options +What the test runs: + testfiles/run_wordle.sh 'lines' 'testfiles/invalid.1' > 6.1.stdout 2> 6.1.stderr +What the test checks: + Standard output (6.1.stdout) should match testfiles/6.1.stdout + + +=== 6.2 - incorrect_guesses02 === +Description: Multiple invalid guesses, default dictionary and other +options +What the test runs: + testfiles/run_wordle.sh 'guess' 'testfiles/invalid.2' > 6.2.stdout 2> 6.2.stderr +What the test checks: + Standard output (6.2.stdout) should match testfiles/6.2.stdout + Expect exit code: 3 + + +=== 6.3 - incorrect_guesses03 === +Description: Multiple invalid guesses, supplied dictionary and default +other options +What the test runs: + testfiles/run_wordle.sh 'marks' 'testfiles/invalid.3' '/local/courses/csse2310/etc/common-words' > 6.3.stdout 2> 6.3.stderr +What the test checks: + Standard output (6.3.stdout) should match testfiles/6.2.stdout + + +=== 6.4 - incorrect_guesses04 === +Description: Multiple invalid guesses, default dictionary, other args +given +What the test runs: + testfiles/run_wordle.sh 'answer' 'testfiles/invalid.4' '-len' '6' > 6.4.stdout 2> 6.4.stderr +What the test checks: + Standard output (6.4.stdout) should match testfiles/6.4.stdout + + +=== 7.1 - various_capitalisation01 === +Description: Single incorrect guess, default dictionary and other options +What the test runs: + testfiles/run_wordle.sh 'hands' 'testfiles/incorrect.1' > 7.1.stdout 2> 7.1.stderr +What the test checks: + Standard output (7.1.stdout) should match testfiles/7.1.stdout + + +=== 7.2 - various_capitalisation02 === +Description: Multiple incorrect guesses, default dictionary and other +options +What the test runs: + testfiles/run_wordle.sh 'guess' 'testfiles/incorrect.2' > 7.2.stdout 2> 7.2.stderr +What the test checks: + Standard output (7.2.stdout) should match testfiles/7.2.stdout + + +=== 7.3 - various_capitalisation03 === +Description: Multiple incorrect guesses, supplied dictionary and default +other options +What the test runs: + testfiles/run_wordle.sh 'marks' 'testfiles/incorrect.3' '/local/courses/csse2310/etc/common-words' > 7.3.stdout 2> 7.3.stderr +What the test checks: + Standard output (7.3.stdout) should match testfiles/7.3.stdout + + +=== 7.4 - various_capitalisation04 === +Description: Multiple incorrect guesses, default dictionary, other +args given +What the test runs: + testfiles/run_wordle.sh 'answer' 'testfiles/incorrect.4' '-len' '6' > 7.4.stdout 2> 7.4.stderr +What the test checks: + Standard output (7.4.stdout) should match testfiles/7.4.stdout + + +=== 8.1 - running_out_of_attempts01 === +Description: Multiple incorrect guesses, default dictionary and other +options +What the test runs: + testfiles/run_wordle.sh 'guess' 'testfiles/incorrect.2' > 8.1.stdout 2> 8.1.stderr +What the test checks: + Expect exit code: 3 + Standard output (8.1.stdout) should match testfiles/8.1.stdout + Standard error (8.1.stderr) should match testfiles/8.1.stderr + + +=== 8.2 - running_out_of_attempts02 === +Description: Multiple incorrect guesses, supplied dictionary and max +3 guesses +What the test runs: + testfiles/run_wordle.sh 'marks' 'testfiles/incorrect.3' '-max' '3' '/local/courses/csse2310/etc/common-words' > 8.2.stdout 2> 8.2.stderr +What the test checks: + Expect exit code: 3 + Standard output (8.2.stdout) should match testfiles/8.2.stdout + Standard error (8.2.stderr) should match testfiles/8.2.stderr + + +=== 8.3 - running_out_of_attempts03 === +Description: Multiple incorrect guesses, default dictionary, word length 6 +What the test runs: + testfiles/run_wordle.sh 'answer' 'testfiles/incorrect.4' '-len' '6' > 8.3.stdout 2> 8.3.stderr +What the test checks: + Expect exit code: 3 + Standard output (8.3.stdout) should match testfiles/8.3.stdout + Standard error (8.3.stderr) should match testfiles/8.3.stderr + + +=== 9.1 - handles_eof01 === +Description: Single blank line input before EOF +What the test runs: + testfiles/run_wordle.sh 'right' 'testfiles/eof.1' > 9.1.stdout 2> 9.1.stderr +What the test checks: + Expect exit code: 3 + Standard output (9.1.stdout) should match testfiles/9.1.stdout + Standard error (9.1.stderr) should match testfiles/9.1.stderr + + +=== 9.2 - handles_eof02 === +Description: EOF after second guess (before newline) +What the test runs: + testfiles/run_wordle.sh 'right' 'testfiles/eof.2' > 9.2.stdout 2> 9.2.stderr +What the test checks: + Expect exit code: 3 + Standard output (9.2.stdout) should match testfiles/9.2.stdout + Standard error (9.2.stderr) should match testfiles/9.1.stderr + + +=== 10.1 - right_answer01 === +Description: Correct answer on third guess +What the test runs: + testfiles/run_wordle.sh 'right' 'testfiles/right.1' > 10.1.stdout 2> 10.1.stderr +What the test checks: + Expect exit code: 0 + Standard output (10.1.stdout) should match testfiles/10.1.stdout + Standard error (10.1.stderr) should match testfiles/empty + + +=== 10.2 - right_answer02 === +Description: Correct answer on third guess - mixed capitalisation +What the test runs: + testfiles/run_wordle.sh 'right' 'testfiles/right.2' > 10.2.stdout 2> 10.2.stderr +What the test checks: + Expect exit code: 0 + Standard output (10.2.stdout) should match testfiles/10.1.stdout + Standard error (10.2.stderr) should match testfiles/empty + + +---------------------------------------------------------------------- +Cleaning up ... +Done +==2665080== Memcheck, a memory error detector +==2665080== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. +==2665080== Using Valgrind-3.17.0 and LibVEX; rerun with -h for copyright info +==2665080== Command: ./wordle-debug 100words.txt +==2665080== +Bad luck - the word is "diary". +Welcome to Wordle! +Enter a 5 letter word (6 attempts remaining): +==2665080== +==2665080== HEAP SUMMARY: +==2665080== in use at exit: 0 bytes in 0 blocks +==2665080== total heap usage: 138 allocs, 138 frees, 34,477 bytes allocated +==2665080== +==2665080== All heap blocks were freed -- no leaks are possible +==2665080== +==2665080== For lists of detected and suppressed errors, rerun with: -s +==2665080== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) diff --git a/checka1.sh b/checka1.sh new file mode 100644 index 0000000..70e6e1a --- /dev/null +++ b/checka1.sh @@ -0,0 +1 @@ +{ make wordle wordle-debug; style.sh; testa1.sh; testa1.sh explain; valgrind --leak-check=full --show-leak-kinds=all ./wordle-debug 100words.txt; } > a1results.txt 2>&1 diff --git a/empty.txt b/empty.txt new file mode 100644 index 0000000..e69de29 diff --git a/testa1.sh b/testa1.sh new file mode 100644 index 0000000..719e4f2 --- /dev/null +++ b/testa1.sh @@ -0,0 +1,1056 @@ +#!/bin/bash +# This script was automatically generated: Sat Mar 19 22:08:41 AEST 2022 +# by uqpsutto + +OUTPUT_DIR=$(date +"/tmp/2310test%Y%m%d-%H%M%S") + +# Environment Variables: +export LD_LIBRARY_PATH='/local/courses/csse2310/lib' + +# Common functions + +# Error handling backtrace code (modified from +# https://stackoverflow.com/questions/25492953) +function backtrace () { + local deptn=${#FUNCNAME[@]} + + for ((i=2; i<$deptn; i++)); do + local func="${FUNCNAME[$i]}" + local line="${BASH_LINENO[$((i-1))]}" + local src="${BASH_SOURCE[$i]}" + printf '%*s' $i '' >&2 # indent + echo "at: $func(), $src, line $line" >&2 + done +} + +set -o errtrace + +# Usage: error_exit +# Prints message to stderr and exits with the given code +function error_exit() { + code="$1" + shift + echo "${bold}$@${normal}" >&2 + backtrace + exit ${code} +} + +# Usage assert [-code ] [-m ] ... +# Checks that the execution of the given command results in a "true" result +# If not, prints the given message (or the command that failed) and +# exits with the given code (or 1) +function assert() { + unset message + code=1 # Default exit code + while true; do + case "$1" in + "-code") code="$2"; shift 2;; + "-m") message="$2"; shift 2;; + *) break;; + esac + done + # Run the command to check + if ! "$@" &>/dev/null ; then + if isset message ; then + error_exit "${code}" "$message" + else + error_exit "${code}" "Assertion failed (${code}): ${*@Q}" + fi + fi +} + +# Usage: isset +# Returns true if variable name is set, false otherwise +function isset() { + #!1 does indirection - name replaced by $1 + #+x will substitute variable value (if set) by x, if unset no change + test -n "${!1+x}" +} + +# Usage: is_integer +# Returns true if the given string is a non-negative integer, false otherwise +function is_integer() { + [[ $1 =~ ^[0-9]+$ ]] +} + +# Usage: is_number +# Returns true if the given string is a non-negative integer or a floating +# point number expressed with a decimal point and optional digits after the +# decimal point. Returns false otherwise. +function is_number() { + [[ $1 =~ ^[0-9]+(\.[0-9]*)?$ ]] +} + +# Usage: is_testid +# Returns true if the given string is of the form category-num.test-num. +# (No leading zeroes permitted.) +# Returns false otherwise. +function is_testid() { + [[ $1 =~ ^[1-9][0-9]*\.[1-9][0-9]*$ ]] +} + +# Usage: is_command +# Checks whether the given string is an executable command +function is_command() { + type "$1" &>/dev/null +} + +# Usage: get_test_num_from_id +# testid should be of the form category-number.test-number. (If not, this +# function exits the program.) Prints the test-number component after the +# decimal point. +function get_test_num_from_id() { + assert is_testid $1 + echo "$1" | cut -d . -f 2 +} + +# Usage: get_category_num_from_id +# testid should be of the form category-number.test-number. (If not, this +# function exits the program.) Prints the category-number component before +# the decimal point. +function get_category_num_from_id() { + assert is_testid $1 + echo "$1" | cut -d . -f 1 +} + +# Usage: indent +# Reads stdin, and echoes to stdout with 4 spaces at the start of each line +function indent() { + sed 's/^/ /' +} + +# Definitions of terminal colour and styling constants +# +#Based on the Stack Overflow answer at +# https://unix.stackexchange.com/questions/9957/how-to-check-if-bash-can-print-colors + +if test -t 1 ; then + # stdout is a tty + normal="$(tput sgr0)" + bold="$(tput bold)" + underline="$(tput smul)" + # Foreground colours + black="$(tput setaf 0)" + red="$(tput setaf 1)" + green="$(tput setaf 2)" + yellow="$(tput setaf 3)" + blue="$(tput setaf 4)" + magenta="$(tput setaf 5)" + cyan="$(tput setaf 6)" + white="$(tput setaf 7)" +else + normal="" + bold="" + underline="" + black="" + red="" + green="" + yellow="" + blue="" + magenta="" + cyan="" + white="" +fi + +# Functions and definitions used by testing scripts + +# By default we send fd 100 to /dev/null. (Sent elsewhere in autograde scripts.) +jsonfd=100 +eval "exec ${jsonfd}>/dev/null" + +# Usage: cleanup +# Function is called on exit +function cleanup() { + echo "Cleaning up ..." + rm -f -r ${OUTPUT_DIR} + echo "Done" +} + +/local/courses/csse2310/bin/csse2310logusage 2>/dev/null + +trap cleanup EXIT + +# Usage: check_usage command-line-arguments +# Prints the action to be taken: run_test or explain_test or mark_test +# (mark_test is the default if the command name begins with mark or autograde) +# Overwrites TESTS_TO_RUN if tests are listed on the command line +function check_usage() { + if [[ $(basename "$0") =~ ^(mark|repmark|autograde).* ]] ; then + action=mark_test + extrausagetext='|mark' + else + action=run_test + fi + if [ "$1" ] ; then + # At least one argument supplied - it must be "run", "explain" or "mark" + case "$1" in + run) action="run_test";; + explain) action="explain_test";; + mark) action="mark_test";; + *) echo "Usage: $0 [run|explain${extrausagetext} [testid ...]]" >&2 + exit 1 + ;; + esac + if [ "$2" ] ; then + # Have one or more arguments - tests to run + shift + # expand any single numbers to all subtests + rm -f /tmp/tests_to_run + touch /tmp/tests_to_run + for i in "$@" ; do + if is_integer "$i" ; then + echo $TESTS_TO_RUN | tr ' ' '\n' | grep "^${i}\." >> /tmp/tests_to_run + else + echo $i >> /tmp/tests_to_run + fi + done + TESTS_TO_RUN=$(cat /tmp/tests_to_run) + rm -f /tmp/tests_to_run + fi + fi +} + +# Usage: setup_for_testing +# Creates a symlink to the testfiles and creates the output directory +function setup_for_testing() { + assert isset TESTFILES_DIR + assert isset OUTPUT_DIR + + echo "Setting up for tests" + echo "Time: $(TZ=AEST-10 date)" + if [ -f .username ] ; then + echo "User: $(cat .username)" + fi + if [ -L testfiles ] ; then + rm testfiles || error_exit 1 "Unable to remove existing testfiles link" + fi + ln -s -T "${TESTFILES_DIR}" testfiles &>/dev/null || + error_exit 1 "Unable to create symbolic link 'testfiles'" + mkdir -p "${OUTPUT_DIR}" || + error_exit 1 "Unable to create temporary output directory" +} + +# Usage: do_svn_checkout +function do_svn_checkout() { + echo "${bold}Checking out code from SVN repo to $2 ...${normal}" + if [ ! "${SVN_REVISION}" ] ; then + SVN_REVISION=head + fi + if ! svn checkout --depth files "$1" -r ${SVN_REVISION} "$2"; then + error_exit 2 "Failed to checkout repository" + fi +} + +# Usage: do_make +function do_make() { + echo "${bold}Running make...${normal}" + if ! make ; then + error_exit 3 "Make failed" + fi +} + +# Usage: run_cmd_and_save_output ... +# Stdout is saved to /.stdout +# Stderr is saved to /.stderr +# Exit code is returned +# cmd may include environment variable definitions +function run_cmd_and_save_output() { + # Check timeout value is valid + assert -code 11 is_number "$1" + timeout="$1" + # Check testid has valid format (num.num) + assert -code 12 is_testid "$2" + testid="$2" + # Make sure the output directory already exists + assert -code 13 test -d "$3" + outputdir="$3" + shift 3 # Consume the first three arguments + # Run the command - with a timeout + # We run in a subshell and send shell error messages to /dev/null + # We first determine whether any of the arguments are setting envirovnment + # variables and we set those + ( + if isset FILESIZELIMIT ; then + ulimit -f ${FILESIZELIMIT} + fi + while [[ "$1" =~ ^[A-Za-z][A-Za-z0-9_]*=.*$ ]] ; do + export ${1%%=*}=${1#*=} + shift + done + exec 2>/dev/null; timeout ${timeout} "${@}" \ + > "${outputdir}/${testid}.stdout" \ + 2> "${outputdir}/${testid}.stderr" + ) + # Will return with exit code of this last command +} + +# Usage: compare_files stdout|stderr|filename \ +# +# Compares the given files and outputs summary line if differ +# Returns true (0) if files both match, false otherwise +function compare_files() { + assert -code 16 test -f "$2" + if [ ! -f "$3" ] ; then + echo "Expected output file $1 not found" + return 1 + elif ! diff -q "$2" "$3" &>/dev/null; then + if [ "$1" = "stdout" -o "$1" = "stderr" ] ; then + echo "Mismatch on $1" + else + echo "Mismatch in file $1" + fi + return 1 + else + return 0 + fi +} + +# Usage: check_program_output command stdout-file stderr-file +# Expect the command to return true - print message if not. +# The given file containing stdout from the test will be supplied as stdin +# for program. The given file containing stderr from the test will be supplied +# on file desciptor 3. +function check_program_output() { + if ! "$1" <"$2" 3<"$3"&>/dev/null; then + echo "Program '$1' returned false" + return 1; + else + return 0; + fi +} + +# Usage: explain_exitcode +# Prints an explanation of an erroneous code with a preceding space - or +# no explanation if the code is not erroneous +function explain_exitcode() { + assert -code 20 is_integer "$1" + actual="$1" + if [[ $actual -eq 124 ]] ; then + explanation=" (Timeout)" + elif [[ $actual -eq 126 ]] ; then + explanation=" (Command not executable)" + elif [[ $actual -eq 127 ]] ; then + explanation=" (Command not found)" + elif [[ $actual -eq 135 ]] ; then + explanation=" (Bus Error)" + elif [[ $actual -eq 139 ]] ; then + explanation=" (Segmentation fault)" + elif [[ $actual -eq 153 ]] ; then + explanation=" (File size limit (${FILESIZELIMIT}k) exceeded)" + elif [[ $actual -ge 129 && $actual -le 160 ]] ; then + explanation=" (SIG"$(kill -l $(($actual - 128)) 2>/dev/null)" signal)" + else + explanation="" + fi + echo ${explanation} +} + +# Usage: compare_exitcodes +# Compares the two exit codes - outputs a summary if they differ +# Returns true (0) if they match, false otherwise +function compare_exitcodes() { + assert -code 20 is_integer "$1" + assert -code 21 is_integer "$2" + expected=$1 + actual=$2 + # Expected exit code should be less than 124 + assert -code 22 test $expected -lt 124 + if [[ $actual -ne $expected ]] ; then + explanation=$(explain_exitcode $actual) + echo "Got exitcode ${actual}${explanation} - but expected ${expected}" + return 1 + else + return 0 + fi +} + +# Usage run_test +# [-exit ] +# [-stdout ] +# [-stderr ] +# [-file ] +# [-prog command] ... +# ... +# Note argument format is the same as for explain_test below so that they +# can be called with the one command. +function run_test() { + assert -code 25 is_testid "$1" + testid="$1" + testname="$2" + testdescription="$3" # Not needed in this function + shift 3 + assert -code 28 is_number "$TIMEOUT" + + unset check expected progs + declare -a check + declare -A expected + declare -a progs + while [ "${1:0:1}" = "-" ] ; do + case "$1" in + "-exit" ) + expected[exit]="$2" + assert -code 26 is_integer "$2" + assert -code 29 [ "$2" -lt 124 ] + shift 2 + ;; + "-stdout" ) + check+=(stdout) + expected[stdout]="$2" + assert -code 27 test -f "$2" + shift 2 + ;; + "-stderr" ) + check+=(stderr) + expected[stderr]="$2" + assert -code 27 test -f "$2" + shift 2 + ;; + "-file" ) + check+=("$2") + expected["$2"]="$3" + assert -code 27 test -f "$3" + shift 3 + ;; + "-prog" ) + progs+=("$2") + assert -code 27 is_command "$2" + shift 2 + ;; + *) + error_exit 1 "Illegal argument to run_test: $1" + esac + done + outputdir="$1" + shift + + printf "%-5s - %-52s" "${testid}" "${testname}" + + run_cmd_and_save_output ${TIMEOUT} ${testid} "${outputdir}" "$@" + actual_exitcode=$? + + rm -f "${outputdir}/.details" + touch "${outputdir}/.details" + + declare -i result=0 # Declare result to be an integer + if [ "${expected[exit]}" ] ; then + compare_exitcodes ${expected[exit]} ${actual_exitcode} \ + >> "${outputdir}/.details" + result+=$? + elif [[ "$actual_exitcode" -ge 124 ]] ; then + error_explanation=$(explain_exitcode "$actual_exitcode") + if [ "${error_explanation}" ] ; then + # We didn't have to check an exit code - but we got a + # timeout or command not found or signal... + result+=1 + echo "Got unexpected exitcode ${actual_exitcode}${error_explanation}" \ + >> "${outputdir}/.details" + fi + fi + if [[ "$actual_exitcode" -lt 124 ]] ; then + # Check files as required + for file in "${check[@]}" ; do + if [ "$file" = "stdout" -o "$file" = "stderr" ] ; then + # Check stdout or stderr + compare_files $file "${expected[$file]}" \ + "${outputdir}/${testid}.${file}" >> "${outputdir}/.details" + result+=$? + else + # Check file + compare_files $file "${expected[$file]}" \ + "${file}" >> "${outputdir}/.details" + result+=$? + fi + done + # Check programs + for prog in "${progs[@]}" ; do + check_program_output "${prog}" "${outputdir}/${testid}.stdout" \ + "${outputdir}/${testid}.stderr" >> "${outputdir}/.details" + result+=$? + done + fi + if [[ "$result" -eq 0 ]] ; then + echo "${bold}${green}PASS${normal}" + else + echo "${bold}${red}FAIL${normal}" + cat "${outputdir}/.details" | indent + fi + rm -f "${outputdir}/.details" +} + +# Usage: explain_test +# [-exit ] +# [-stdout ] +# [-stderr ] +# [-file ] +# [-prog command] +# ... +function explain_test() { + assert -code 30 is_testid "$1" + testid="$1" + testname="$2" + test_description="$3" + shift 3 + echo "${green}${bold}=== ${testid} - ${testname} ===${normal}" + echo "${bold}Description:${normal} ${test_description}" | fmt + msg="" + while [ "${1:0:1}" = "-" ] ; do + case "$1" in + "-exit" ) + msg+=" Expect exit code: $2"$'\n'; + assert -code 31 is_integer "$2" + shift 2;; + "-stdout" ) + msg+=" Standard output (${testid}.stdout) should match $2"$'\n' + shift 2;; + "-stderr" ) + msg+=" Standard error (${testid}.stderr) should match $2"$'\n' + shift 2;; + "-file" ) + msg+=" Contents of file '$2' should match $3"$'\n' + shift 3;; + "-prog" ) + msg+=" Result of running '$2' should be true (exit status 0)"$'\n' + shift 2;; + * ) + error_exit 1 "Illegal argument to explain_test: $1" + esac + done + + outputdir="$1" + cmd="$2" + shift 2 + echo "${bold}What the test runs:${normal}" + echo " ${cmd} ${*@Q} > ${testid}.stdout 2>" \ + "${testid}.stderr" + echo "${bold}What the test checks:${normal}" + echo "${msg}" + echo +} + +# Usage: do_tests +function do_tests() { + if [[ "${action}" =~ ^(run|mark).* ]] ; then + # Action is to run or mark tests, not explain tests - make sure we have a + # program to test + do_make + echo "${bold}Running tests ... ${normal}" + fi + echo "----------------------------------------------------------------------" + for i in $TESTS_TO_RUN ; do + cmd="test${i}" + if [[ $(type -t ${cmd}) = "function" ]]; then + "${cmd}" "${action}" + else + error_exit 4 "Test ${i} is not known" + fi + done + echo "----------------------------------------------------------------------" +} + +# Tests for category 1 - invalid_usage +# Details for test 1.1. ($1 will be run_test or explain_test) +function test1.1() { + "$1" 1.1 "invalid_usage01" "Check that args 'one two' considered invalid" \ + -exit 1 \ + -stdout "testfiles/empty" \ + -stderr "testfiles/1.1.stderr" \ + "${OUTPUT_DIR}" \ + "./wordle-debug" 'one' 'two' +} + +# Details for test 1.2. ($1 will be run_test or explain_test) +function test1.2() { + "$1" 1.2 "invalid_usage02" "Check that args 'one two three' considered invalid" \ + -exit 1 \ + -stdout "testfiles/empty" \ + -stderr "testfiles/1.1.stderr" \ + "${OUTPUT_DIR}" \ + "./wordle-debug" 'one' 'two' 'three' +} + +# Details for test 1.3. ($1 will be run_test or explain_test) +function test1.3() { + "$1" 1.3 "invalid_usage03" "Check that args 'one two three four' considered invalid" \ + -exit 1 \ + -stdout "testfiles/empty" \ + -stderr "testfiles/1.1.stderr" \ + "${OUTPUT_DIR}" \ + "./wordle-debug" 'one' 'two' 'three' 'four' +} + +# Details for test 1.4. ($1 will be run_test or explain_test) +function test1.4() { + "$1" 1.4 "invalid_usage04" "Check that args 'one two three four five' considered invalid" \ + -exit 1 \ + -stdout "testfiles/empty" \ + -stderr "testfiles/1.1.stderr" \ + "${OUTPUT_DIR}" \ + "./wordle-debug" 'one' 'two' 'three' 'four' 'five' +} + +# Details for test 1.5. ($1 will be run_test or explain_test) +function test1.5() { + "$1" 1.5 "invalid_usage05" "Check that args 'one two three four five six' considered invalid" \ + -exit 1 \ + -stdout "testfiles/empty" \ + -stderr "testfiles/1.1.stderr" \ + "${OUTPUT_DIR}" \ + "./wordle-debug" 'one' 'two' 'three' 'four' 'five' 'six' +} + +# Details for test 1.6. ($1 will be run_test or explain_test) +function test1.6() { + "$1" 1.6 "invalid_usage06" "Check that args '-max' considered invalid" \ + -exit 1 \ + -stdout "testfiles/empty" \ + -stderr "testfiles/1.1.stderr" \ + "${OUTPUT_DIR}" \ + "./wordle-debug" '-max' +} + +# Details for test 1.7. ($1 will be run_test or explain_test) +function test1.7() { + "$1" 1.7 "invalid_usage07" "Check that args '-len' considered invalid" \ + -exit 1 \ + -stdout "testfiles/empty" \ + -stderr "testfiles/1.1.stderr" \ + "${OUTPUT_DIR}" \ + "./wordle-debug" '-len' +} + +# Details for test 1.8. ($1 will be run_test or explain_test) +function test1.8() { + "$1" 1.8 "invalid_usage08" "Check that args '-max nonnumber' considered invalid" \ + -exit 1 \ + -stdout "testfiles/empty" \ + -stderr "testfiles/1.1.stderr" \ + "${OUTPUT_DIR}" \ + "./wordle-debug" '-max' 'nonnumber' +} + +# Details for test 1.9. ($1 will be run_test or explain_test) +function test1.9() { + "$1" 1.9 "invalid_usage09" "Check that args '-len nonnumber' considered invalid" \ + -exit 1 \ + -stdout "testfiles/empty" \ + -stderr "testfiles/1.1.stderr" \ + "${OUTPUT_DIR}" \ + "./wordle-debug" '-len' 'nonnumber' +} + +# Details for test 1.10. ($1 will be run_test or explain_test) +function test1.10() { + "$1" 1.10 "invalid_usage10" "Check that args '-max 5 -len' considered invalid" \ + -exit 1 \ + -stdout "testfiles/empty" \ + -stderr "testfiles/1.1.stderr" \ + "${OUTPUT_DIR}" \ + "./wordle-debug" '-max' '5' '-len' +} + +# Details for test 1.11. ($1 will be run_test or explain_test) +function test1.11() { + "$1" 1.11 "invalid_usage11" "Check that args '-max 2' considered invalid" \ + -exit 1 \ + -stdout "testfiles/empty" \ + -stderr "testfiles/1.1.stderr" \ + "${OUTPUT_DIR}" \ + "./wordle-debug" '-max' '2' +} + +# Details for test 1.12. ($1 will be run_test or explain_test) +function test1.12() { + "$1" 1.12 "invalid_usage12" "Check that args '-max 10' considered invalid" \ + -exit 1 \ + -stdout "testfiles/empty" \ + -stderr "testfiles/1.1.stderr" \ + "${OUTPUT_DIR}" \ + "./wordle-debug" '-max' '10' +} + +# Details for test 1.13. ($1 will be run_test or explain_test) +function test1.13() { + "$1" 1.13 "invalid_usage13" "Check that args '-max 3abc' considered invalid" \ + -exit 1 \ + -stdout "testfiles/empty" \ + -stderr "testfiles/1.1.stderr" \ + "${OUTPUT_DIR}" \ + "./wordle-debug" '-max' '3abc' +} + +# Details for test 1.14. ($1 will be run_test or explain_test) +function test1.14() { + "$1" 1.14 "invalid_usage14" "Check that args '-max 3 dictionary extra-arg' considered invalid" \ + -exit 1 \ + -stdout "testfiles/empty" \ + -stderr "testfiles/1.1.stderr" \ + "${OUTPUT_DIR}" \ + "./wordle-debug" '-max' '3' 'dictionary' 'extra-arg' +} + +# Details for test 1.15. ($1 will be run_test or explain_test) +function test1.15() { + "$1" 1.15 "invalid_usage15" "Check that args '-max 8 -len nonnumber' considered invalid" \ + -exit 1 \ + -stdout "testfiles/empty" \ + -stderr "testfiles/1.1.stderr" \ + "${OUTPUT_DIR}" \ + "./wordle-debug" '-max' '8' '-len' 'nonnumber' +} + +# Details for test 1.16. ($1 will be run_test or explain_test) +function test1.16() { + "$1" 1.16 "invalid_usage16" "Check that args '-max 8 -max 8' considered invalid" \ + -exit 1 \ + -stdout "testfiles/empty" \ + -stderr "testfiles/1.1.stderr" \ + "${OUTPUT_DIR}" \ + "./wordle-debug" '-max' '8' '-max' '8' +} + +# Details for test 1.17. ($1 will be run_test or explain_test) +function test1.17() { + "$1" 1.17 "invalid_usage17" "Check that args '-guess' considered invalid" \ + -exit 1 \ + -stdout "testfiles/empty" \ + -stderr "testfiles/1.1.stderr" \ + "${OUTPUT_DIR}" \ + "./wordle-debug" '-guess' +} + +# Details for test 1.18. ($1 will be run_test or explain_test) +function test1.18() { + "$1" 1.18 "invalid_usage18" "Check that args '-max 3 -max 5 /usr/share/dict/words' considered invalid" \ + -exit 1 \ + -stdout "testfiles/empty" \ + -stderr "testfiles/1.1.stderr" \ + "${OUTPUT_DIR}" \ + "./wordle-debug" '-max' '3' '-max' '5' '/usr/share/dict/words' +} + +# Details for test 1.19. ($1 will be run_test or explain_test) +function test1.19() { + "$1" 1.19 "invalid_usage19" "Check that args '-max 5 -len 5 /usr/share/dict/words extra' considered invalid" \ + -exit 1 \ + -stdout "testfiles/empty" \ + -stderr "testfiles/1.1.stderr" \ + "${OUTPUT_DIR}" \ + "./wordle-debug" '-max' '5' '-len' '5' '/usr/share/dict/words' 'extra' +} + +# Details for test 1.20. ($1 will be run_test or explain_test) +function test1.20() { + "$1" 1.20 "invalid_usage20" "Check that args '-guess 8' considered invalid" \ + -exit 1 \ + -stdout "testfiles/empty" \ + -stderr "testfiles/1.1.stderr" \ + "${OUTPUT_DIR}" \ + "./wordle-debug" '-guess' '8' +} + +# Tests for category 2 - invalid_file +# Details for test 2.1. ($1 will be run_test or explain_test) +function test2.1() { + "$1" 2.1 "invalid_file01" "Dictionary 'hello.there.csse2310' unable to be opened with program args ''" \ + -exit 2 \ + -stdout "testfiles/empty" \ + -stderr "testfiles/2.1.stderr" \ + "${OUTPUT_DIR}" \ + "./wordle-debug" 'hello.there.csse2310' +} + +# Details for test 2.2. ($1 will be run_test or explain_test) +function test2.2() { + "$1" 2.2 "invalid_file02" "Dictionary '/does/not/exist' unable to be opened with program args ''" \ + -exit 2 \ + -stdout "testfiles/empty" \ + -stderr "testfiles/2.2.stderr" \ + "${OUTPUT_DIR}" \ + "./wordle-debug" '/does/not/exist' +} + +# Details for test 2.3. ($1 will be run_test or explain_test) +function test2.3() { + "$1" 2.3 "invalid_file03" "Dictionary '/var/log/cron' unable to be opened with program args ''" \ + -exit 2 \ + -stdout "testfiles/empty" \ + -stderr "testfiles/2.3.stderr" \ + "${OUTPUT_DIR}" \ + "./wordle-debug" '/var/log/cron' +} + +# Details for test 2.4. ($1 will be run_test or explain_test) +function test2.4() { + "$1" 2.4 "invalid_file04" "Dictionary '../nope' unable to be opened with program args ''" \ + -exit 2 \ + -stdout "testfiles/empty" \ + -stderr "testfiles/2.4.stderr" \ + "${OUTPUT_DIR}" \ + "./wordle-debug" '../nope' +} + +# Details for test 2.5. ($1 will be run_test or explain_test) +function test2.5() { + "$1" 2.5 "invalid_file05" "Dictionary 'hello.there.csse2310' unable to be opened with program args '-len 4'" \ + -exit 2 \ + -stdout "testfiles/empty" \ + -stderr "testfiles/2.5.stderr" \ + "${OUTPUT_DIR}" \ + "./wordle-debug" '-len' '4' 'hello.there.csse2310' +} + +# Details for test 2.6. ($1 will be run_test or explain_test) +function test2.6() { + "$1" 2.6 "invalid_file06" "Dictionary '/does/not/exist' unable to be opened with program args '-len 4'" \ + -exit 2 \ + -stdout "testfiles/empty" \ + -stderr "testfiles/2.6.stderr" \ + "${OUTPUT_DIR}" \ + "./wordle-debug" '-len' '4' '/does/not/exist' +} + +# Details for test 2.7. ($1 will be run_test or explain_test) +function test2.7() { + "$1" 2.7 "invalid_file07" "Dictionary '/var/log/cron' unable to be opened with program args '-len 4'" \ + -exit 2 \ + -stdout "testfiles/empty" \ + -stderr "testfiles/2.7.stderr" \ + "${OUTPUT_DIR}" \ + "./wordle-debug" '-len' '4' '/var/log/cron' +} + +# Details for test 2.8. ($1 will be run_test or explain_test) +function test2.8() { + "$1" 2.8 "invalid_file08" "Dictionary '../nope' unable to be opened with program args '-len 4'" \ + -exit 2 \ + -stdout "testfiles/empty" \ + -stderr "testfiles/2.8.stderr" \ + "${OUTPUT_DIR}" \ + "./wordle-debug" '-len' '4' '../nope' +} + +# Tests for category 3 - prompt +# Details for test 3.1. ($1 will be run_test or explain_test) +function test3.1() { + "$1" 3.1 "prompt01" "Check for welcome and prompt" \ + -stdout "testfiles/3.1.stdout" \ + "${OUTPUT_DIR}" \ + "testfiles-custom/run_wordle.sh" 'right' '/dev/null' '-lines' '2' +} + +# Details for test 3.2. ($1 will be run_test or explain_test) +function test3.2() { + "$1" 3.2 "prompt02" "Check for welcome and prompt 2" \ + -stdout "testfiles/3.2.stdout" \ + "${OUTPUT_DIR}" \ + "testfiles-custom/run_wordle.sh" 'right' '/dev/null' '-lines' '2' '-max' '3' +} + +# Tests for category 4 - wrong_len +# Details for test 4.1. ($1 will be run_test or explain_test) +function test4.1() { + "$1" 4.1 "wrong_len01" "Check guess of length 4 with default word length" \ + -stdout "testfiles/4.1.stdout" \ + "${OUTPUT_DIR}" \ + "testfiles-custom/run_wordle.sh" 'right' 'testfiles/wronglen.1' +} + +# Details for test 4.2. ($1 will be run_test or explain_test) +function test4.2() { + "$1" 4.2 "wrong_len02" "Check guess of length 0 with default word length" \ + -stdout "testfiles/4.1.stdout" \ + "${OUTPUT_DIR}" \ + "testfiles-custom/run_wordle.sh" 'right' 'testfiles/wronglen.2' +} + +# Details for test 4.3. ($1 will be run_test or explain_test) +function test4.3() { + "$1" 4.3 "wrong_len03" "Check guesses that are too long (word length 4)" \ + -stdout "testfiles/4.3.stdout" \ + "${OUTPUT_DIR}" \ + "testfiles-custom/run_wordle.sh" 'word' 'testfiles/wronglen.3' '-len' '4' +} + +# Tests for category 5 - non_letters +# Details for test 5.1. ($1 will be run_test or explain_test) +function test5.1() { + "$1" 5.1 "non_letters01" "Check program handles guesses with non letters - with args '-len 6 -max 7'" \ + -stdout "testfiles/5.1.stdout" \ + "${OUTPUT_DIR}" \ + "testfiles-custom/run_wordle.sh" 'right' 'testfiles/nonletters.1' +} + +# Details for test 5.2. ($1 will be run_test or explain_test) +function test5.2() { + "$1" 5.2 "non_letters02" "Check program handles guesses with non letters - with args '-len 6 -max 7'" \ + -stdout "testfiles/5.2.stdout" \ + "${OUTPUT_DIR}" \ + "testfiles-custom/run_wordle.sh" 'there' 'testfiles/nonletters.2' +} + +# Details for test 5.3. ($1 will be run_test or explain_test) +function test5.3() { + "$1" 5.3 "non_letters03" "Check program handles guesses with non letters - with args '-len 6 -max 7'" \ + -stdout "testfiles/5.3.stdout" \ + "${OUTPUT_DIR}" \ + "testfiles-custom/run_wordle.sh" 'cat' 'testfiles/nonletters.3' '-len' '3' +} + +# Tests for category 6 - incorrect_guesses +# Details for test 6.1. ($1 will be run_test or explain_test) +function test6.1() { + "$1" 6.1 "incorrect_guesses01" "Single invalid guess, default dictionary and other options" \ + -stdout "testfiles/6.1.stdout" \ + "${OUTPUT_DIR}" \ + "testfiles-custom/run_wordle.sh" 'lines' 'testfiles/invalid.1' +} + +# Details for test 6.2. ($1 will be run_test or explain_test) +function test6.2() { + "$1" 6.2 "incorrect_guesses02" "Multiple invalid guesses, default dictionary and other options" \ + -stdout "testfiles/6.2.stdout" \ + -exit 3 \ + "${OUTPUT_DIR}" \ + "testfiles-custom/run_wordle.sh" 'guess' 'testfiles/invalid.2' +} + +# Details for test 6.3. ($1 will be run_test or explain_test) +function test6.3() { + "$1" 6.3 "incorrect_guesses03" "Multiple invalid guesses, supplied dictionary and default other options" \ + -stdout "testfiles/6.2.stdout" \ + "${OUTPUT_DIR}" \ + "testfiles-custom/run_wordle.sh" 'marks' 'testfiles/invalid.3' '/local/courses/csse2310/etc/common-words' +} + +# Details for test 6.4. ($1 will be run_test or explain_test) +function test6.4() { + "$1" 6.4 "incorrect_guesses04" "Multiple invalid guesses, default dictionary, other args given" \ + -stdout "testfiles/6.4.stdout" \ + "${OUTPUT_DIR}" \ + "testfiles-custom/run_wordle.sh" 'answer' 'testfiles/invalid.4' '-len' '6' +} + +# Tests for category 7 - various_capitalisation +# Details for test 7.1. ($1 will be run_test or explain_test) +function test7.1() { + "$1" 7.1 "various_capitalisation01" "Single incorrect guess, default dictionary and other options" \ + -stdout "testfiles/7.1.stdout" \ + "${OUTPUT_DIR}" \ + "testfiles-custom/run_wordle.sh" 'hands' 'testfiles/incorrect.1' +} + +# Details for test 7.2. ($1 will be run_test or explain_test) +function test7.2() { + "$1" 7.2 "various_capitalisation02" "Multiple incorrect guesses, default dictionary and other options" \ + -stdout "testfiles/7.2.stdout" \ + "${OUTPUT_DIR}" \ + "testfiles-custom/run_wordle.sh" 'guess' 'testfiles/incorrect.2' +} + +# Details for test 7.3. ($1 will be run_test or explain_test) +function test7.3() { + "$1" 7.3 "various_capitalisation03" "Multiple incorrect guesses, supplied dictionary and default other options" \ + -stdout "testfiles/7.3.stdout" \ + "${OUTPUT_DIR}" \ + "testfiles-custom/run_wordle.sh" 'marks' 'testfiles/incorrect.3' '/local/courses/csse2310/etc/common-words' +} + +# Details for test 7.4. ($1 will be run_test or explain_test) +function test7.4() { + "$1" 7.4 "various_capitalisation04" "Multiple incorrect guesses, default dictionary, other args given" \ + -stdout "testfiles/7.4.stdout" \ + "${OUTPUT_DIR}" \ + "testfiles-custom/run_wordle.sh" 'answer' 'testfiles/incorrect.4' '-len' '6' +} + +# Tests for category 8 - running_out_of_attempts +# Details for test 8.1. ($1 will be run_test or explain_test) +function test8.1() { + "$1" 8.1 "running_out_of_attempts01" "Multiple incorrect guesses, default dictionary and other options" \ + -exit 3 \ + -stdout "testfiles/8.1.stdout" \ + -stderr "testfiles/8.1.stderr" \ + "${OUTPUT_DIR}" \ + "testfiles-custom/run_wordle.sh" 'guess' 'testfiles/incorrect.2' +} + +# Details for test 8.2. ($1 will be run_test or explain_test) +function test8.2() { + "$1" 8.2 "running_out_of_attempts02" "Multiple incorrect guesses, supplied dictionary and max 3 guesses" \ + -exit 3 \ + -stdout "testfiles/8.2.stdout" \ + -stderr "testfiles/8.2.stderr" \ + "${OUTPUT_DIR}" \ + "testfiles-custom/run_wordle.sh" 'marks' 'testfiles/incorrect.3' '-max' '3' '/local/courses/csse2310/etc/common-words' +} + +# Details for test 8.3. ($1 will be run_test or explain_test) +function test8.3() { + "$1" 8.3 "running_out_of_attempts03" "Multiple incorrect guesses, default dictionary, word length 6" \ + -exit 3 \ + -stdout "testfiles/8.3.stdout" \ + -stderr "testfiles/8.3.stderr" \ + "${OUTPUT_DIR}" \ + "testfiles-custom/run_wordle.sh" 'answer' 'testfiles/incorrect.4' '-len' '6' +} + +# Tests for category 9 - handles_eof +# Details for test 9.1. ($1 will be run_test or explain_test) +function test9.1() { + "$1" 9.1 "handles_eof01" "Single blank line input before EOF" \ + -exit 3 \ + -stdout "testfiles/9.1.stdout" \ + -stderr "testfiles/9.1.stderr" \ + "${OUTPUT_DIR}" \ + "testfiles-custom/run_wordle.sh" 'right' 'testfiles/eof.1' +} + +# Details for test 9.2. ($1 will be run_test or explain_test) +function test9.2() { + "$1" 9.2 "handles_eof02" "EOF after second guess (before newline)" \ + -exit 3 \ + -stdout "testfiles/9.2.stdout" \ + -stderr "testfiles/9.1.stderr" \ + "${OUTPUT_DIR}" \ + "testfiles-custom/run_wordle.sh" 'right' 'testfiles/eof.2' +} + +# Tests for category 10 - right_answer +# Details for test 10.1. ($1 will be run_test or explain_test) +function test10.1() { + "$1" 10.1 "right_answer01" "Correct answer on third guess" \ + -exit 0 \ + -stdout "testfiles/10.1.stdout" \ + -stderr "testfiles/empty" \ + "${OUTPUT_DIR}" \ + "testfiles-custom/run_wordle.sh" 'right' 'testfiles/right.1' +} + +# Details for test 10.2. ($1 will be run_test or explain_test) +function test10.2() { + "$1" 10.2 "right_answer02" "Correct answer on third guess - mixed capitalisation" \ + -exit 0 \ + -stdout "testfiles/10.1.stdout" \ + -stderr "testfiles/empty" \ + "${OUTPUT_DIR}" \ + "testfiles-custom/run_wordle.sh" 'right' 'testfiles/right.2' +} + +# Tests for category 11 - repeated_letters +# Tests for category 12 - memory +TIMEOUT=10 +TESTS_TO_RUN=' 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 1.10 1.11 1.12 1.13 1.14 1.15 1.16 1.17 1.18 1.19 1.20 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 3.1 3.2 4.1 4.2 4.3 5.1 5.2 5.3 6.1 6.2 6.3 6.4 7.1 7.2 7.3 7.4 8.1 8.2 8.3 9.1 9.2 10.1 10.2 ' + +check_usage "$@" +# Now have defined 'action' and 'TESTS_TO_RUN' +# 'action' will be one of 'run_test', 'explain_test' or 'mark_test' + +TESTFILES_DIR='/local/courses/csse2310/resources/a1/testfiles.public' + +# If we're running tests or marking then get setup to do so +if [ $action = run_test -o $action = mark_test ] ; then + setup_for_testing +fi + +do_tests + diff --git a/testfiles-custom/1.1.stderr b/testfiles-custom/1.1.stderr new file mode 100644 index 0000000..b82acab --- /dev/null +++ b/testfiles-custom/1.1.stderr @@ -0,0 +1 @@ +Usage: wordle [-len word-length] [-max max-guesses] [dictionary] diff --git a/testfiles-custom/10.1.stdout b/testfiles-custom/10.1.stdout new file mode 100644 index 0000000..8167078 --- /dev/null +++ b/testfiles-custom/10.1.stdout @@ -0,0 +1,7 @@ +Welcome to Wordle! +Enter a 5 letter word (6 attempts remaining): +RI--T +Enter a 5 letter word (5 attempts remaining): +-r--g +Enter a 5 letter word (4 attempts remaining): +Correct! diff --git a/testfiles-custom/2.1.stderr b/testfiles-custom/2.1.stderr new file mode 100644 index 0000000..1fe1c84 --- /dev/null +++ b/testfiles-custom/2.1.stderr @@ -0,0 +1 @@ +wordle: dictionary file "hello.there.csse2310" cannot be opened diff --git a/testfiles-custom/2.2.stderr b/testfiles-custom/2.2.stderr new file mode 100644 index 0000000..35c48c5 --- /dev/null +++ b/testfiles-custom/2.2.stderr @@ -0,0 +1 @@ +wordle: dictionary file "/does/not/exist" cannot be opened diff --git a/testfiles-custom/2.3.stderr b/testfiles-custom/2.3.stderr new file mode 100644 index 0000000..ac919a1 --- /dev/null +++ b/testfiles-custom/2.3.stderr @@ -0,0 +1 @@ +wordle: dictionary file "/var/log/cron" cannot be opened diff --git a/testfiles-custom/2.4.stderr b/testfiles-custom/2.4.stderr new file mode 100644 index 0000000..5e873e7 --- /dev/null +++ b/testfiles-custom/2.4.stderr @@ -0,0 +1 @@ +wordle: dictionary file "../nope" cannot be opened diff --git a/testfiles-custom/2.5.stderr b/testfiles-custom/2.5.stderr new file mode 100644 index 0000000..1fe1c84 --- /dev/null +++ b/testfiles-custom/2.5.stderr @@ -0,0 +1 @@ +wordle: dictionary file "hello.there.csse2310" cannot be opened diff --git a/testfiles-custom/2.6.stderr b/testfiles-custom/2.6.stderr new file mode 100644 index 0000000..35c48c5 --- /dev/null +++ b/testfiles-custom/2.6.stderr @@ -0,0 +1 @@ +wordle: dictionary file "/does/not/exist" cannot be opened diff --git a/testfiles-custom/2.7.stderr b/testfiles-custom/2.7.stderr new file mode 100644 index 0000000..ac919a1 --- /dev/null +++ b/testfiles-custom/2.7.stderr @@ -0,0 +1 @@ +wordle: dictionary file "/var/log/cron" cannot be opened diff --git a/testfiles-custom/2.8.stderr b/testfiles-custom/2.8.stderr new file mode 100644 index 0000000..5e873e7 --- /dev/null +++ b/testfiles-custom/2.8.stderr @@ -0,0 +1 @@ +wordle: dictionary file "../nope" cannot be opened diff --git a/testfiles-custom/3.1.stderr b/testfiles-custom/3.1.stderr new file mode 100644 index 0000000..f452435 --- /dev/null +++ b/testfiles-custom/3.1.stderr @@ -0,0 +1 @@ +Bad luck - the word is "right". diff --git a/testfiles-custom/3.1.stdout b/testfiles-custom/3.1.stdout new file mode 100644 index 0000000..3a74119 --- /dev/null +++ b/testfiles-custom/3.1.stdout @@ -0,0 +1,2 @@ +Welcome to Wordle! +Enter a 5 letter word (6 attempts remaining): diff --git a/testfiles-custom/3.2.stdout b/testfiles-custom/3.2.stdout new file mode 100644 index 0000000..60f8bad --- /dev/null +++ b/testfiles-custom/3.2.stdout @@ -0,0 +1,2 @@ +Welcome to Wordle! +Enter a 5 letter word (3 attempts remaining): diff --git a/testfiles-custom/4.1.stdout b/testfiles-custom/4.1.stdout new file mode 100644 index 0000000..5dcc5f8 --- /dev/null +++ b/testfiles-custom/4.1.stdout @@ -0,0 +1,4 @@ +Welcome to Wordle! +Enter a 5 letter word (6 attempts remaining): +Words must be 5 letters long - try again. +Enter a 5 letter word (6 attempts remaining): diff --git a/testfiles-custom/4.3.stderr b/testfiles-custom/4.3.stderr new file mode 100644 index 0000000..be56a9b --- /dev/null +++ b/testfiles-custom/4.3.stderr @@ -0,0 +1 @@ +Bad luck - the word is "word". diff --git a/testfiles-custom/4.3.stdout b/testfiles-custom/4.3.stdout new file mode 100644 index 0000000..2afedea --- /dev/null +++ b/testfiles-custom/4.3.stdout @@ -0,0 +1,14 @@ +Welcome to Wordle! +Enter a 4 letter word (6 attempts remaining): +Words must be 4 letters long - try again. +Enter a 4 letter word (6 attempts remaining): +Words must be 4 letters long - try again. +Enter a 4 letter word (6 attempts remaining): +Words must be 4 letters long - try again. +Enter a 4 letter word (6 attempts remaining): +Words must be 4 letters long - try again. +Enter a 4 letter word (6 attempts remaining): +Words must be 4 letters long - try again. +Enter a 4 letter word (6 attempts remaining): +Words must be 4 letters long - try again. +Enter a 4 letter word (6 attempts remaining): diff --git a/testfiles-custom/5.1.stderr b/testfiles-custom/5.1.stderr new file mode 100644 index 0000000..f452435 --- /dev/null +++ b/testfiles-custom/5.1.stderr @@ -0,0 +1 @@ +Bad luck - the word is "right". diff --git a/testfiles-custom/5.1.stdout b/testfiles-custom/5.1.stdout new file mode 100644 index 0000000..6b95c27 --- /dev/null +++ b/testfiles-custom/5.1.stdout @@ -0,0 +1,4 @@ +Welcome to Wordle! +Enter a 5 letter word (6 attempts remaining): +Words must contain only letters - try again. +Enter a 5 letter word (6 attempts remaining): diff --git a/testfiles-custom/5.2.stderr b/testfiles-custom/5.2.stderr new file mode 100644 index 0000000..55faa3c --- /dev/null +++ b/testfiles-custom/5.2.stderr @@ -0,0 +1 @@ +Bad luck - the word is "there". diff --git a/testfiles-custom/5.2.stdout b/testfiles-custom/5.2.stdout new file mode 100644 index 0000000..9142558 --- /dev/null +++ b/testfiles-custom/5.2.stdout @@ -0,0 +1,18 @@ +Welcome to Wordle! +Enter a 5 letter word (6 attempts remaining): +Words must contain only letters - try again. +Enter a 5 letter word (6 attempts remaining): +Words must contain only letters - try again. +Enter a 5 letter word (6 attempts remaining): +Words must contain only letters - try again. +Enter a 5 letter word (6 attempts remaining): +Words must contain only letters - try again. +Enter a 5 letter word (6 attempts remaining): +Words must contain only letters - try again. +Enter a 5 letter word (6 attempts remaining): +Words must contain only letters - try again. +Enter a 5 letter word (6 attempts remaining): +Words must contain only letters - try again. +Enter a 5 letter word (6 attempts remaining): +Words must contain only letters - try again. +Enter a 5 letter word (6 attempts remaining): diff --git a/testfiles-custom/5.3.stderr b/testfiles-custom/5.3.stderr new file mode 100644 index 0000000..07e7f4b --- /dev/null +++ b/testfiles-custom/5.3.stderr @@ -0,0 +1 @@ +Bad luck - the word is "cat". diff --git a/testfiles-custom/5.3.stdout b/testfiles-custom/5.3.stdout new file mode 100644 index 0000000..f579faf --- /dev/null +++ b/testfiles-custom/5.3.stdout @@ -0,0 +1,10 @@ +Welcome to Wordle! +Enter a 3 letter word (6 attempts remaining): +Words must contain only letters - try again. +Enter a 3 letter word (6 attempts remaining): +Words must contain only letters - try again. +Enter a 3 letter word (6 attempts remaining): +Words must contain only letters - try again. +Enter a 3 letter word (6 attempts remaining): +Words must contain only letters - try again. +Enter a 3 letter word (6 attempts remaining): diff --git a/testfiles-custom/6.1.stderr b/testfiles-custom/6.1.stderr new file mode 100644 index 0000000..6e53d85 --- /dev/null +++ b/testfiles-custom/6.1.stderr @@ -0,0 +1 @@ +Bad luck - the word is "lines". diff --git a/testfiles-custom/6.1.stdout b/testfiles-custom/6.1.stdout new file mode 100644 index 0000000..0c4e4bd --- /dev/null +++ b/testfiles-custom/6.1.stdout @@ -0,0 +1,4 @@ +Welcome to Wordle! +Enter a 5 letter word (6 attempts remaining): +Word not found in the dictionary - try again. +Enter a 5 letter word (6 attempts remaining): diff --git a/testfiles-custom/6.2.stderr b/testfiles-custom/6.2.stderr new file mode 100644 index 0000000..4816b2b --- /dev/null +++ b/testfiles-custom/6.2.stderr @@ -0,0 +1 @@ +Bad luck - the word is "guess". diff --git a/testfiles-custom/6.2.stdout b/testfiles-custom/6.2.stdout new file mode 100644 index 0000000..8ae795b --- /dev/null +++ b/testfiles-custom/6.2.stdout @@ -0,0 +1,12 @@ +Welcome to Wordle! +Enter a 5 letter word (6 attempts remaining): +Word not found in the dictionary - try again. +Enter a 5 letter word (6 attempts remaining): +Word not found in the dictionary - try again. +Enter a 5 letter word (6 attempts remaining): +Word not found in the dictionary - try again. +Enter a 5 letter word (6 attempts remaining): +Word not found in the dictionary - try again. +Enter a 5 letter word (6 attempts remaining): +Word not found in the dictionary - try again. +Enter a 5 letter word (6 attempts remaining): diff --git a/testfiles-custom/6.3.stderr b/testfiles-custom/6.3.stderr new file mode 100644 index 0000000..ad52be6 --- /dev/null +++ b/testfiles-custom/6.3.stderr @@ -0,0 +1 @@ +Bad luck - the word is "marks". diff --git a/testfiles-custom/6.4.stderr b/testfiles-custom/6.4.stderr new file mode 100644 index 0000000..1b1aa05 --- /dev/null +++ b/testfiles-custom/6.4.stderr @@ -0,0 +1 @@ +Bad luck - the word is "answer". diff --git a/testfiles-custom/6.4.stdout b/testfiles-custom/6.4.stdout new file mode 100644 index 0000000..7a1c29e --- /dev/null +++ b/testfiles-custom/6.4.stdout @@ -0,0 +1,16 @@ +Welcome to Wordle! +Enter a 6 letter word (6 attempts remaining): +Word not found in the dictionary - try again. +Enter a 6 letter word (6 attempts remaining): +Word not found in the dictionary - try again. +Enter a 6 letter word (6 attempts remaining): +Word not found in the dictionary - try again. +Enter a 6 letter word (6 attempts remaining): +Word not found in the dictionary - try again. +Enter a 6 letter word (6 attempts remaining): +Word not found in the dictionary - try again. +Enter a 6 letter word (6 attempts remaining): +Word not found in the dictionary - try again. +Enter a 6 letter word (6 attempts remaining): +Word not found in the dictionary - try again. +Enter a 6 letter word (6 attempts remaining): diff --git a/testfiles-custom/7.1.stderr b/testfiles-custom/7.1.stderr new file mode 100644 index 0000000..447133b --- /dev/null +++ b/testfiles-custom/7.1.stderr @@ -0,0 +1 @@ +Bad luck - the word is "hands". diff --git a/testfiles-custom/7.1.stdout b/testfiles-custom/7.1.stdout new file mode 100644 index 0000000..bc2cfb9 --- /dev/null +++ b/testfiles-custom/7.1.stdout @@ -0,0 +1,4 @@ +Welcome to Wordle! +Enter a 5 letter word (6 attempts remaining): +----- +Enter a 5 letter word (5 attempts remaining): diff --git a/testfiles-custom/7.2.stderr b/testfiles-custom/7.2.stderr new file mode 100644 index 0000000..4816b2b --- /dev/null +++ b/testfiles-custom/7.2.stderr @@ -0,0 +1 @@ +Bad luck - the word is "guess". diff --git a/testfiles-custom/7.2.stdout b/testfiles-custom/7.2.stdout new file mode 100644 index 0000000..3223d01 --- /dev/null +++ b/testfiles-custom/7.2.stdout @@ -0,0 +1,12 @@ +Welcome to Wordle! +Enter a 5 letter word (6 attempts remaining): +s---- +Enter a 5 letter word (5 attempts remaining): +--E-- +Enter a 5 letter word (4 attempts remaining): +GU--S +Enter a 5 letter word (3 attempts remaining): +GU--S +Enter a 5 letter word (2 attempts remaining): +sUg-- +Enter a 5 letter word (last attempt): diff --git a/testfiles-custom/7.3.stderr b/testfiles-custom/7.3.stderr new file mode 100644 index 0000000..ad52be6 --- /dev/null +++ b/testfiles-custom/7.3.stderr @@ -0,0 +1 @@ +Bad luck - the word is "marks". diff --git a/testfiles-custom/7.3.stdout b/testfiles-custom/7.3.stdout new file mode 100644 index 0000000..b95bd67 --- /dev/null +++ b/testfiles-custom/7.3.stdout @@ -0,0 +1,10 @@ +Welcome to Wordle! +Enter a 5 letter word (6 attempts remaining): +--RKS +Enter a 5 letter word (5 attempts remaining): +--RKS +Enter a 5 letter word (4 attempts remaining): +sk-r- +Enter a 5 letter word (3 attempts remaining): +----- +Enter a 5 letter word (2 attempts remaining): diff --git a/testfiles-custom/7.4.stderr b/testfiles-custom/7.4.stderr new file mode 100644 index 0000000..1b1aa05 --- /dev/null +++ b/testfiles-custom/7.4.stderr @@ -0,0 +1 @@ +Bad luck - the word is "answer". diff --git a/testfiles-custom/7.4.stdout b/testfiles-custom/7.4.stdout new file mode 100644 index 0000000..8959ba8 --- /dev/null +++ b/testfiles-custom/7.4.stdout @@ -0,0 +1,12 @@ +Welcome to Wordle! +Enter a 6 letter word (6 attempts remaining): +------ +Enter a 6 letter word (5 attempts remaining): +e--an- +Enter a 6 letter word (4 attempts remaining): +e----- +Enter a 6 letter word (3 attempts remaining): +----ER +Enter a 6 letter word (2 attempts remaining): +--n-ER +Enter a 6 letter word (last attempt): diff --git a/testfiles-custom/8.1.stderr b/testfiles-custom/8.1.stderr new file mode 100644 index 0000000..4816b2b --- /dev/null +++ b/testfiles-custom/8.1.stderr @@ -0,0 +1 @@ +Bad luck - the word is "guess". diff --git a/testfiles-custom/8.1.stdout b/testfiles-custom/8.1.stdout new file mode 100644 index 0000000..3223d01 --- /dev/null +++ b/testfiles-custom/8.1.stdout @@ -0,0 +1,12 @@ +Welcome to Wordle! +Enter a 5 letter word (6 attempts remaining): +s---- +Enter a 5 letter word (5 attempts remaining): +--E-- +Enter a 5 letter word (4 attempts remaining): +GU--S +Enter a 5 letter word (3 attempts remaining): +GU--S +Enter a 5 letter word (2 attempts remaining): +sUg-- +Enter a 5 letter word (last attempt): diff --git a/testfiles-custom/8.2.stderr b/testfiles-custom/8.2.stderr new file mode 100644 index 0000000..ad52be6 --- /dev/null +++ b/testfiles-custom/8.2.stderr @@ -0,0 +1 @@ +Bad luck - the word is "marks". diff --git a/testfiles-custom/8.2.stdout b/testfiles-custom/8.2.stdout new file mode 100644 index 0000000..b9f11ec --- /dev/null +++ b/testfiles-custom/8.2.stdout @@ -0,0 +1,7 @@ +Welcome to Wordle! +Enter a 5 letter word (3 attempts remaining): +--RKS +Enter a 5 letter word (2 attempts remaining): +--RKS +Enter a 5 letter word (last attempt): +sk-r- diff --git a/testfiles-custom/8.3.stderr b/testfiles-custom/8.3.stderr new file mode 100644 index 0000000..1b1aa05 --- /dev/null +++ b/testfiles-custom/8.3.stderr @@ -0,0 +1 @@ +Bad luck - the word is "answer". diff --git a/testfiles-custom/8.3.stdout b/testfiles-custom/8.3.stdout new file mode 100644 index 0000000..8959ba8 --- /dev/null +++ b/testfiles-custom/8.3.stdout @@ -0,0 +1,12 @@ +Welcome to Wordle! +Enter a 6 letter word (6 attempts remaining): +------ +Enter a 6 letter word (5 attempts remaining): +e--an- +Enter a 6 letter word (4 attempts remaining): +e----- +Enter a 6 letter word (3 attempts remaining): +----ER +Enter a 6 letter word (2 attempts remaining): +--n-ER +Enter a 6 letter word (last attempt): diff --git a/testfiles-custom/9.1.stderr b/testfiles-custom/9.1.stderr new file mode 100644 index 0000000..f452435 --- /dev/null +++ b/testfiles-custom/9.1.stderr @@ -0,0 +1 @@ +Bad luck - the word is "right". diff --git a/testfiles-custom/9.1.stdout b/testfiles-custom/9.1.stdout new file mode 100644 index 0000000..5dcc5f8 --- /dev/null +++ b/testfiles-custom/9.1.stdout @@ -0,0 +1,4 @@ +Welcome to Wordle! +Enter a 5 letter word (6 attempts remaining): +Words must be 5 letters long - try again. +Enter a 5 letter word (6 attempts remaining): diff --git a/testfiles-custom/9.2.stdout b/testfiles-custom/9.2.stdout new file mode 100644 index 0000000..f3348df --- /dev/null +++ b/testfiles-custom/9.2.stdout @@ -0,0 +1,6 @@ +Welcome to Wordle! +Enter a 5 letter word (6 attempts remaining): +--r-- +Enter a 5 letter word (5 attempts remaining): +i---T +Enter a 5 letter word (4 attempts remaining): diff --git a/testfiles-custom/empty b/testfiles-custom/empty new file mode 100644 index 0000000..e69de29 diff --git a/testfiles-custom/eof.1 b/testfiles-custom/eof.1 new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/testfiles-custom/eof.1 @@ -0,0 +1 @@ + diff --git a/testfiles-custom/eof.2 b/testfiles-custom/eof.2 new file mode 100644 index 0000000..89bd7af --- /dev/null +++ b/testfiles-custom/eof.2 @@ -0,0 +1,2 @@ +marks +input \ No newline at end of file diff --git a/testfiles-custom/incorrect.1 b/testfiles-custom/incorrect.1 new file mode 100644 index 0000000..9af8670 --- /dev/null +++ b/testfiles-custom/incorrect.1 @@ -0,0 +1 @@ +tiler diff --git a/testfiles-custom/incorrect.2 b/testfiles-custom/incorrect.2 new file mode 100644 index 0000000..c39cfda --- /dev/null +++ b/testfiles-custom/incorrect.2 @@ -0,0 +1,5 @@ +shift +breaD +gulps +GuLpS +suGAr diff --git a/testfiles-custom/incorrect.3 b/testfiles-custom/incorrect.3 new file mode 100644 index 0000000..427ce24 --- /dev/null +++ b/testfiles-custom/incorrect.3 @@ -0,0 +1,4 @@ +works +WORKS +SKIRT +youNG diff --git a/testfiles-custom/incorrect.4 b/testfiles-custom/incorrect.4 new file mode 100644 index 0000000..98f731b --- /dev/null +++ b/testfiles-custom/incorrect.4 @@ -0,0 +1,5 @@ +uphold +expand +eXcIte +BOTHER +HuNtEr diff --git a/testfiles-custom/invalid.1 b/testfiles-custom/invalid.1 new file mode 100644 index 0000000..9336dcd --- /dev/null +++ b/testfiles-custom/invalid.1 @@ -0,0 +1 @@ +iouyj diff --git a/testfiles-custom/invalid.2 b/testfiles-custom/invalid.2 new file mode 100644 index 0000000..6df75f1 --- /dev/null +++ b/testfiles-custom/invalid.2 @@ -0,0 +1,5 @@ +asdef +frozg +LHSgd +ASDFM +NEnVM diff --git a/testfiles-custom/invalid.3 b/testfiles-custom/invalid.3 new file mode 100644 index 0000000..74243dd --- /dev/null +++ b/testfiles-custom/invalid.3 @@ -0,0 +1,5 @@ +caulk +CAULK +abOde +ALoft +wipED diff --git a/testfiles-custom/invalid.4 b/testfiles-custom/invalid.4 new file mode 100644 index 0000000..b4d076e --- /dev/null +++ b/testfiles-custom/invalid.4 @@ -0,0 +1,7 @@ +SIXsix +LONNNG +WOORDS +Whichh +AARREE +NOTTTT +validd diff --git a/testfiles-custom/nonletters.1 b/testfiles-custom/nonletters.1 new file mode 100644 index 0000000..a92c70c --- /dev/null +++ b/testfiles-custom/nonletters.1 @@ -0,0 +1 @@ +90210 diff --git a/testfiles-custom/nonletters.2 b/testfiles-custom/nonletters.2 new file mode 100644 index 0000000..080ca8c --- /dev/null +++ b/testfiles-custom/nonletters.2 @@ -0,0 +1,8 @@ +don't +SS-10 +12345 +#2344 +he11o +now!! +why?! +$7231 diff --git a/testfiles-custom/nonletters.3 b/testfiles-custom/nonletters.3 new file mode 100644 index 0000000..19c3a69 --- /dev/null +++ b/testfiles-custom/nonletters.3 @@ -0,0 +1,4 @@ +123 +%$# +(*) +I'm diff --git a/testfiles-custom/right.1 b/testfiles-custom/right.1 new file mode 100644 index 0000000..e7bfbb5 --- /dev/null +++ b/testfiles-custom/right.1 @@ -0,0 +1,3 @@ +rivet +wrong +right diff --git a/testfiles-custom/right.2 b/testfiles-custom/right.2 new file mode 100644 index 0000000..bd0fdd6 --- /dev/null +++ b/testfiles-custom/right.2 @@ -0,0 +1,5 @@ +RivEt +wRoNg +RigHt +wORDs +valiD diff --git a/testfiles-custom/run_wordle.sh b/testfiles-custom/run_wordle.sh new file mode 100644 index 0000000..d89271e --- /dev/null +++ b/testfiles-custom/run_wordle.sh @@ -0,0 +1,37 @@ +#!/bin/bash +# Usage: run_wordle.sh answer guess-file [-lines n] [args] +# Runs wordle with the given word as the answer with stdin redirected from +# the given guess file (one guess per line). +# If the -lines argument is given, then only the given number of lines +# from standard output will be saved - others will be discarded. +# Additional args are given as command line arguments to wordle. +if [ "$#" -lt 2 ] ; then + echo "Insufficient arguments to $0" >&2 + exit 1 +fi +if [ ! -r "$2" ] ; then + echo "Can't read guesses file \"$2\"" >&2 + exit 2 +fi +answer="$1" +guesses="$2" +shift 2 +if [ "$1" = "-lines" -a -n "$2" ] ; then + lines="$2" + truncatemsg=0 + shift 2; +else + # We truncate output after 200 lines - more than enough + lines=200 + truncatemsg=1 +fi +export WORD2310="${answer}" +# Run the program and only output the first $lines lines. +${wordle:=./wordle-debug} "$@" < ${guesses} | \ + awk "NR<=${lines} \ + {print \$0} \ + NR==${lines}+1&&${truncatemsg} \ + {print \"Output truncated - exceeds ${lines} lines\"}" +# Get wordle's exit status (first command in the pipeline) +status=${PIPESTATUS[0]} +exit $status diff --git a/testfiles-custom/wronglen.1 b/testfiles-custom/wronglen.1 new file mode 100644 index 0000000..a0cd9bb --- /dev/null +++ b/testfiles-custom/wronglen.1 @@ -0,0 +1 @@ +byte diff --git a/testfiles-custom/wronglen.2 b/testfiles-custom/wronglen.2 new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/testfiles-custom/wronglen.2 @@ -0,0 +1 @@ + diff --git a/testfiles-custom/wronglen.3 b/testfiles-custom/wronglen.3 new file mode 100644 index 0000000..1f314d2 --- /dev/null +++ b/testfiles-custom/wronglen.3 @@ -0,0 +1,6 @@ +these +words +short +andsomeofthemarenotwords +ANDsomeOFthemUSEupperCASEletters +AndOneOfTheWordsIsReallyLonnnnnnnnnnnnnnnnnnnnnnng diff --git a/wordle.c b/wordle.c new file mode 100644 index 0000000..3d221a2 --- /dev/null +++ b/wordle.c @@ -0,0 +1,368 @@ +#include "wordle.h" + +// TODO: Use enums for error codes + +char* get_flag_from_argument(char* argument) { + char* flag = (char*)malloc(sizeof(char) * (strlen(argument) + 1)); + if (!strcpy(flag, argument)) { + fprintf(stderr, "Could not parse argument \"%s\", exiting...\n", + argument); + exit(E_ARGS); + } + return flag; +} + +int add_to_used_flags( + char* flag, char usedFlags[MAX_FLAGS][5], int* usedFlagIndex) { + // Make sure command has not already been used + for (int i = 0; i < (*usedFlagIndex) + 1; i++) { + if (strcmp(usedFlags[i], flag) == 0) { + return 0; + } + } + + // Add command to used commands to make sure there are no + // duplicates + strcpy(usedFlags[*usedFlagIndex], flag); + (*usedFlagIndex)++; + return 1; +} + +int check_valid_value(char* value) { + for (int i = 0; i < strlen(value); i++) { + if (!isdigit(value[i])) { + return 0; + } + } + return 1; +} + +void update_file_path_from_argument(char** filePath, char* argument) { + *filePath = + (char*)realloc(*filePath, sizeof(char) * (strlen(argument) + 1)); + if (!strcpy(*filePath, argument)) { + fprintf(stderr, "Could not parse file path, exiting...\n"); + exit(1); + } +} + +int get_valid_command_arguments(int argc, char* argv[], int* wordLength, + int* maxGuesses, char** filePath) { + char usedFlags[MAX_FLAGS][5]; + int usedFlagIndex = 0; + char* flag = NULL; + + for (int i = 1; i < argc; i++) { + flag = get_flag_from_argument(argv[i]); + if (flag[0] == '-') { + int value = 0; + + // Check conditions and move index to argument index + if (!add_to_used_flags(flag, usedFlags, &usedFlagIndex) || + (++i) >= argc) { + free(flag); + return 0; + } + + // Check if the argument is a valid integer + value = atoi(argv[i]); + if (!check_valid_value(argv[i]) || !value || value < MIN_RANGE || + value > MAX_RANGE) { + return 0; + } + + // If the option matches, update its corresponding value + if (strcmp(flag, "-len") == 0) { + *wordLength = value; + } else if (strcmp(flag, "-max") == 0) { + *maxGuesses = value; + } else { + free(flag); + return 0; + } + } else if (i == argc - 1) { + update_file_path_from_argument(filePath, argv[i]); + } else { + free(flag); + return 0; + } + } + if (flag) { + free(flag); + } + return 1; +} + +int check_invalid_word(char* word, int wordLength) { + if (strlen(word) != wordLength) { + return 1; + } + + for (int i = 0; i < strlen(word); i++) { + if (!isalpha(word[i])) { + return 2; + } + } + return 0; +} + +void free_dictionary(Dictionary* dictionary) { + for (int i = 0; i < dictionary->size; i++) { + free(dictionary->contents[i]); + } + free(dictionary->contents); +} + +void insert_word_into_dictionary(Dictionary* dictionary, char* word) { + // Increase dictionary size + dictionary->size++; + dictionary->contents = (char**)realloc( + dictionary->contents, sizeof(char*) * dictionary->size); + + // Allocate space and insert new word + dictionary->contents[dictionary->size - 1] = + (char*)malloc(sizeof(char) * (strlen(word) + 1)); + strcpy(dictionary->contents[dictionary->size - 1], word); +} + +Dictionary get_new_dictionary() { + Dictionary dictionary; + dictionary.size = 0; + dictionary.contents = (char**)malloc(sizeof(char*)); + return dictionary; +} + +Dictionary get_dictionary_from_buffer(FILE* buffer) { + Dictionary dictionary = get_new_dictionary(); + + char line[MAX_GUESS_LENGTH] = {'\0'}; + while (fgets(line, MAX_GUESS_LENGTH, buffer)) { + // Remove newline + line[strcspn(line, "\n")] = '\0'; + + if (check_invalid_word(line, strlen(line))) { + continue; + } + string_lowercase(line); + + // Won't check if word in already as lookup times would be huge + insert_word_into_dictionary(&dictionary, line); + } + return dictionary; +} + +Dictionary get_dictionary_words_of_length( + Dictionary dictionary, int wordLength) { + Dictionary sizedDictionary = get_new_dictionary(); + for (int i = 0; i < dictionary.size; i++) { + if (strlen(dictionary.contents[i]) == wordLength) { + insert_word_into_dictionary( + &sizedDictionary, dictionary.contents[i]); + } + } + return sizedDictionary; +} + +int check_word_in_dictionary(Dictionary dictionary, char* word) { + if (dictionary.size == 0) { + return 0; + } + + for (int i = 0; i < dictionary.size; i++) { + if (strcmp(dictionary.contents[i], word) == 0) { + return 1; + } + } + return 0; +} + +char* get_blank_wordle(int wordLength) { + char* wordleWord = (char*)malloc(sizeof(char) * (wordLength + 1)); + memset(wordleWord, '-', wordLength); + wordleWord[wordLength] = '\0'; + return wordleWord; +} + +char* get_wordle_format(char* guessedWord, char* answer) { + char* modifiedAnswer = (char*)malloc(sizeof(char) * (strlen(answer) + 1)); + char* wordleWord = get_blank_wordle(strlen(answer)); + + strcpy(modifiedAnswer, answer); + + // First parse, match all exact matches + for (int i = 0; i < strlen(answer); i++) { + if (guessedWord[i] == modifiedAnswer[i]) { + wordleWord[i] = toupper(modifiedAnswer[i]); + modifiedAnswer[i] = '-'; + } + } + + // Second parse, match everything else + for (int i = 0; i < strlen(answer); i++) { + // If the character has already been matched + if (wordleWord[i] != '-') { + continue; + } + + // Sourced with inspiration from: + // https://stackoverflow.com/questions/1479386/ + const char* ptr; + if ((ptr = strchr(modifiedAnswer, guessedWord[i]))) { + int characterIndex = ptr - modifiedAnswer; + + wordleWord[i] = tolower(guessedWord[i]); + modifiedAnswer[characterIndex] = '-'; + } + } + free(modifiedAnswer); + return wordleWord; +} + +void string_lowercase(char* string) { + for (int i = 0; i < strlen(string); i++) { + string[i] = tolower(string[i]); + } +} + +char* get_guessed_word_from_input(void) { + char* guessedWord = (char*)malloc(sizeof(char) * MAX_GUESS_LENGTH); + + char buffer[MAX_GUESS_LENGTH]; + if (fgets(buffer, MAX_GUESS_LENGTH, stdin)) { + // Remove newline character + buffer[strcspn(buffer, "\n")] = '\0'; + if (strlen(buffer) == 0) { + // Words cannot be smaller than 3 so this is a quick and + // dirty hack + strcpy(buffer, "a"); + } + guessedWord = (char*)realloc( + guessedWord, sizeof(char) * (strlen(buffer) + 1)); + strcpy(guessedWord, buffer); + } else { + free(guessedWord); + return NULL; + } + + return guessedWord; +} + +void prompt_invalid_guess(int invalidType, int wordLength) { + if (invalidType == 1) { + printf("Words must be %d letters long - try again.\n", wordLength); + } else if (invalidType == 2) { + printf("Words must contain only letters - try again.\n"); + } +} + +void print_attempt_prompt(int guessesRemaining, int wordLength) { + if (guessesRemaining > 1) { + printf("Enter a %d letter word (%d attempts remaining):\n", wordLength, + guessesRemaining); + } else if (guessesRemaining == 1) { + printf("Enter a %d letter word (last attempt):\n", wordLength); + } +} + +// TODO: split this into smaller functions +int game_loop( + int wordLength, int maxGuesses, char* answer, Dictionary dictionary) { + int gameWon = 0; + int guessesRemaining = maxGuesses; + printf(WELCOME_MESSAGE); + while (!gameWon && guessesRemaining >= 1) { + int invalidGuess = 1; + + // Print prompt + print_attempt_prompt(guessesRemaining, wordLength); + + // Get user input, break on EOF + char* guessedWord = get_guessed_word_from_input(); + if (!guessedWord) { + free(guessedWord); + break; + } + + // Check if word is valid + invalidGuess = check_invalid_word(guessedWord, wordLength); + if (invalidGuess) { + prompt_invalid_guess(invalidGuess, wordLength); + free(guessedWord); + continue; + } + + string_lowercase(guessedWord); + + // If the guessed word is correct + if (strcmp(guessedWord, answer) == 0) { + printf("Correct!\n"); + gameWon = 1; + free(guessedWord); + return E_OK; + } + + if (!check_word_in_dictionary(dictionary, guessedWord)) { + printf("Word not found in the dictionary - try again.\n"); + free(guessedWord); + continue; + } + + char* wordleWord = get_wordle_format(guessedWord, answer); + printf("%s\n", wordleWord); + + free(wordleWord); + free(guessedWord); + guessesRemaining--; + } + fprintf(stderr, "Bad luck - the word is \"%s\".\n", answer); + return E_LOST; +} + +void get_answer(char** answer, int wordLength) { + char* tmpAnswer = get_random_word(wordLength); + *answer = (char*)realloc(*answer, sizeof(char) * (strlen(tmpAnswer) + 1)); + strcpy(*answer, tmpAnswer); + free(tmpAnswer); +} + +int main(int argc, char* argv[]) { + char* answer = NULL; + int wordLength = DEFAULT_WORD_LENGTH; + int maxGuesses = DEFAULT_MAX_GUESSES; + char* filePath = (char*)malloc( + sizeof(char) * (strlen(DEFAULT_DICTIONARY_PATH) + 1)); + strcpy(filePath, DEFAULT_DICTIONARY_PATH); + + // clang-format off + if (argc > 6 || !get_valid_command_arguments( + argc, argv, &wordLength, &maxGuesses, &filePath)) { + fprintf(stderr, USAGE_MESSAGE); + return E_ARGS; + } + // clang-format on + + FILE* dictionaryBuffer = fopen(filePath, "r"); + if (!dictionaryBuffer) { + fprintf(stderr, FILE_ERROR, filePath); + return E_FILE; + } + free(filePath); + + get_answer(&answer, wordLength); + + // Load dictionary and get words of the required word length + Dictionary dictionary = get_dictionary_from_buffer(dictionaryBuffer); + Dictionary sizedDictionary = + get_dictionary_words_of_length(dictionary, wordLength); + + fclose(dictionaryBuffer); + + int gameResult = + game_loop(wordLength, maxGuesses, answer, sizedDictionary); + + free(answer); + free_dictionary(&dictionary); + free_dictionary(&sizedDictionary); + + return gameResult; +} diff --git a/wordle.h b/wordle.h new file mode 100644 index 0000000..e6a8118 --- /dev/null +++ b/wordle.h @@ -0,0 +1,270 @@ +#ifndef S4638520_WORDLE +#define S4638520_WORDLE +#include +#include +#include +#include +#include + +#define MIN_RANGE 3 +#define MAX_RANGE 9 +#define MAX_FLAGS 2 +// According to spec max guess is 50, so giving some extra space for +// newline, EOF, etc +#define MAX_GUESS_LENGTH 55 + +#define ENVIRONMENT_WORD "WORD2310" + +#define WELCOME_MESSAGE "Welcome to Wordle!\n" +#define USAGE_MESSAGE "Usage: wordle [-len word-length] [-max max-guesses] "\ + "[dictionary]\n" +#define FILE_ERROR "wordle: dictionary file \"%s\" cannot be opened\n" + +#define DEFAULT_WORD_LENGTH 5 +#define DEFAULT_MAX_GUESSES 6 +#define DEFAULT_DICTIONARY_PATH "/usr/share/dict/words" + +enum ErrorCodes { + E_OK = 0, + E_ARGS = 1, + E_FILE = 2, + E_LOST = 3 +}; + +typedef struct Dictionary { + char** contents; + int size; +} Dictionary; + +/* get_flag_from_argument() + * ------------------------ + * Gets the flag from the given argument + * + * argument: the argument + * + * Returns: the flag + */ +char* get_flag_from_argument(char* argument); + +/* add_to_used_flags() + * --------------------- + * Adds the given flag to an array of already used flags, also checks if + * the flag has been used before within the array. + * + * flag: the given flag to add to the array + * usedFlags: the array containing the already contained flags, assumes + * each word is of length 5 including the null terminator. + * usedFlagIndex: the current index of "usedFlags". + * + * Returns: 1 if the function completes successfully, 0 otherwise. + */ +int add_to_used_flags(char* flag, char usedFlags[MAX_FLAGS][5], + int* usedFlagIndex); + +/* check_valid_value() + * -------------------------- + * Check if the entire value given is of an integer + * + * value: the value to check + * + * Returns: 0 if invalid, 1 if valid + */ +int check_valid_value(char* value); + +/* update_file_path_from_value() + * ----------------------------- + * Updates the filePath pointer to point at the new parsed filepath from the + * argument. + * + * filePath: pointer to the string for the filePath + * argument: what to update the filepath to + */ +void update_file_path_from_argument(char** filePath, char* argument); + +/* get_valid_command_arguments() + * --------------------------- + * Parses the given set of flags parsed on the command line and updates + * variables "wordLength", "maxGuesses" and "filepath". + * + * argc: the amount of parsed arguments + * argv: the array containing strings of all parsed arguments + * wordLength: pointer to update for the wordLength flag '-len' + * maxGuesses: pointer to update for the maxGuesses flag '-max' + * filePath: pointer to update the filepath to the dictionary + * + * Returns: + * 0: + * - the flag parsed has been seen before in the command + * - the flag does not have an associated value + * - the flag value is NaN or not within 3 and 9 + * - the flag is not a recognised flag + * - an argument is parsed without a dash and is not at the end of the command + * 1: + * - the above is not true, and hence the function completed successfully + * + * Exits: if strcpy fails at any point during this function + */ +int get_valid_command_arguments(int argc, char** argv, int* wordLength, + int* maxGuesses, char** filePath); + +/* check_invalid_word() + * -------------------- + * Checks if a word is invalid + * + * word: the word to check + * wordLength: the length the word needs to be + * + * Returns: + * 0: if the word is valid + * 1: if the word is not the required length + * 2: if the word has characters that are not alphas + */ +int check_invalid_word(char* word, int wordLength); + +/* free_dictionary() + * ----------------- + * Frees all allocated memory for the dictionary, including its contents + * + * dictionary: the dictionary to free from memory + */ +void free_dictionary(Dictionary* dictionary); + +/* insert_word_into_dictionary() + * ----------------------------- + * Reallocate space for the dictionary, allocate space for word and then + * instert it into the dictionary + * + * dictionary: the dictionary to insert the word into + * word: the word to insert into the dictionary + */ +void insert_word_into_dictionary(Dictionary* dictionary, char* word); + +/* get_new_dictionary() + * -------------------- + * Creates a new dictionary and allocates the needed space for it + * + * Returns: the new dictionary + */ +Dictionary get_new_dictionary(void); + +/* get_dictionary_from_buffer() + * ---------------------------- + * Creates a dictionary based on the parsed file buffer + * + * dictionaryFile: the buffer containing newline seperated words for the + * dictionary + * + * Returns: the dictionary made from the buffer + */ +Dictionary get_dictionary_from_buffer(FILE* dictionaryFile); + +/* get_dictionary_words_of_length() + * -------------------------------- + * Makes a new dictionary from an old containing only words of the given + * length + * + * dictionary: the dictionary containing all of the words + * wordLength: the length of the words to be extracted + * + * Returns: the dictionary containing only words of given length + */ +Dictionary get_dictionary_words_of_length(Dictionary dictionary, + int wordLength); + +/* check_word_in_dictionary() + * -------------------------- + * Checks if the supplied word is in the supplied dictionary + * + * dictionary: the dictionary to check + * word: the word to check if in dictionary + * + * Returns: 0 if it is not, 1 if it is + */ +int check_word_in_dictionary(Dictionary dictionary, char* word); + +/* get_blank_wordle() + * ------------------ + * Gets a string that only contains '-' of given length + * For example, with a length of 5, the string will be "-----" + * + * wordLength: the length of the blank word + * + * Returns: the blank word + * + * Reference: Inspired by https://stackoverflow.com/questions/12352682 + */ +char* get_blank_wordle(int wordLength); + +/* get_wordle_format() + * ------------------- + * Get the guess in the format of wordle + * + * guessedWord: the word the user guessed + * answer: the correct word + * + * Returns: the word in the format of wordle + */ +char* get_wordle_format(char* guessedWord, char* answer); + +/* string_lowercase() + * ------------------ + * Converts a string to its lowercase varient + * + * string: the string to convert + */ +void string_lowercase(char* string); + +/* get_guessed_word_from_input() + * ----------------------------- + * Prompt for a guess word and return the guessed word if it is valid + * + * Returns: + * NULL: if EOF + * "a": if the word is blank + * the input if not the above + */ +char* get_guessed_word_from_input(void); + +/* prompt_invalid_guess() + * ---------------------- + * Prompt if the guess was invalid and the correct message for the type of + * invalidity + * + * invalidType: the type of invalid guess + * wordLength: the length of the answer + */ +void prompt_invalid_guess(int invalidType, int wordLength); + +/* print_attempt_prompt() + * ---------------------- + * Print the attempt with the amount of guesses remaing and length of word + * + * guessesRemaining: the amount of guesses remaining + * wordLength: the length of the answer + */ +void print_attempt_prompt(int guessesRemaining, int wordLength); + +/* game_loop() + * ----------- + * The gameplay loop for the game + * + * wordLength: the maximum length of a word to be given + * maxGuesses: the maximum number of guesses the user has + * + * Returns: + * 0: if the correct word was guessed + * 3: if the user failed + */ +int game_loop(int wordLength, int maxGuesses, char* answer, + Dictionary dictionary); + +/* get_answer() + * ------------ + * Gets and allocates a random word as the answer using get_random_word() + * + * answer: pointer to the answer + * wordLength, the word length if getting a random word + */ +void get_answer(char** answer, int wordLength); + +#endif