commit 1139e086fcf44881a8a9d797e3a4c38fd28d7728 Author: slapelachie Date: Fri Mar 21 10:51:17 2025 +1000 init 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