From 7c6221dd6af7bb40e534051af55e4969b936dc0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A1s=20B=20Nagy?= <20251272+BNAndras@users.noreply.github.com> Date: Sat, 11 Nov 2023 22:17:52 -0800 Subject: [PATCH 1/3] Add nucleotide-count --- config.json | 8 ++ .../nucleotide-count/.docs/instructions.md | 23 ++++ .../nucleotide-count/.meta/Example.cfc | 6 + .../nucleotide-count/.meta/ExampleTest.cfc | 7 ++ .../nucleotide-count/.meta/config.json | 20 ++++ .../practice/nucleotide-count/.meta/hints.md | 3 + .../nucleotide-count/.meta/tests.toml | 25 +++++ .../nucleotide-count/NucleotideCount.cfc | 32 ++++++ .../nucleotide-count/NucleotideCountTest.cfc | 35 ++++++ .../practice/nucleotide-count/TestRunner.cfc | 103 ++++++++++++++++++ exercises/practice/nucleotide-count/box.json | 8 ++ exercises/practice/nucleotide-count/index.cfm | 37 +++++++ 12 files changed, 307 insertions(+) create mode 100644 exercises/practice/nucleotide-count/.docs/instructions.md create mode 100644 exercises/practice/nucleotide-count/.meta/Example.cfc create mode 100644 exercises/practice/nucleotide-count/.meta/ExampleTest.cfc create mode 100644 exercises/practice/nucleotide-count/.meta/config.json create mode 100644 exercises/practice/nucleotide-count/.meta/hints.md create mode 100644 exercises/practice/nucleotide-count/.meta/tests.toml create mode 100644 exercises/practice/nucleotide-count/NucleotideCount.cfc create mode 100644 exercises/practice/nucleotide-count/NucleotideCountTest.cfc create mode 100644 exercises/practice/nucleotide-count/TestRunner.cfc create mode 100644 exercises/practice/nucleotide-count/box.json create mode 100644 exercises/practice/nucleotide-count/index.cfm diff --git a/config.json b/config.json index e86d5ee..adf6422 100644 --- a/config.json +++ b/config.json @@ -304,6 +304,14 @@ "difficulty": 1, "topics": null }, + { + "slug": "nucleotide-count", + "name": "Nucleotide Count", + "uuid": "cd5fd765-2d79-4cb8-b1f0-a5891a149672", + "practices": [], + "prerequisites": [], + "difficulty": 2 + }, { "slug": "queen-attack", "name": "Queen Attack", diff --git a/exercises/practice/nucleotide-count/.docs/instructions.md b/exercises/practice/nucleotide-count/.docs/instructions.md new file mode 100644 index 0000000..548d9ba --- /dev/null +++ b/exercises/practice/nucleotide-count/.docs/instructions.md @@ -0,0 +1,23 @@ +# Instructions + +Each of us inherits from our biological parents a set of chemical instructions known as DNA that influence how our bodies are constructed. +All known life depends on DNA! + +> Note: You do not need to understand anything about nucleotides or DNA to complete this exercise. + +DNA is a long chain of other chemicals and the most important are the four nucleotides, adenine, cytosine, guanine and thymine. +A single DNA chain can contain billions of these four nucleotides and the order in which they occur is important! +We call the order of these nucleotides in a bit of DNA a "DNA sequence". + +We represent a DNA sequence as an ordered collection of these four nucleotides and a common way to do that is with a string of characters such as "ATTACG" for a DNA sequence of 6 nucleotides. +'A' for adenine, 'C' for cytosine, 'G' for guanine, and 'T' for thymine. + +Given a string representing a DNA sequence, count how many of each nucleotide is present. +If the string contains characters that aren't A, C, G, or T then it is invalid and you should signal an error. + +For example: + +```text +"GATTACA" -> 'A': 3, 'C': 1, 'G': 1, 'T': 2 +"INVALID" -> error +``` diff --git a/exercises/practice/nucleotide-count/.meta/Example.cfc b/exercises/practice/nucleotide-count/.meta/Example.cfc new file mode 100644 index 0000000..e26d452 --- /dev/null +++ b/exercises/practice/nucleotide-count/.meta/Example.cfc @@ -0,0 +1,6 @@ +/** +* Here is an example solution for the NucleotideCount exercise +*/ +component { + +} \ No newline at end of file diff --git a/exercises/practice/nucleotide-count/.meta/ExampleTest.cfc b/exercises/practice/nucleotide-count/.meta/ExampleTest.cfc new file mode 100644 index 0000000..5ccb2cd --- /dev/null +++ b/exercises/practice/nucleotide-count/.meta/ExampleTest.cfc @@ -0,0 +1,7 @@ +component extends="NucleotideCountTest" { + + function beforeAll(){ + SUT = createObject( 'Solution' ); + } + +} \ No newline at end of file diff --git a/exercises/practice/nucleotide-count/.meta/config.json b/exercises/practice/nucleotide-count/.meta/config.json new file mode 100644 index 0000000..baf3a0c --- /dev/null +++ b/exercises/practice/nucleotide-count/.meta/config.json @@ -0,0 +1,20 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "NucleotideCount.cfc" + ], + "test": [ + "NucleotideCountTest.cfc" + ], + "example": [ + ".meta/Example.cfc", + ".meta/ExampleTest.cfc" + ] + }, + "blurb": "Given a DNA string, compute how many times each nucleotide occurs in the string.", + "source": "The Calculating DNA Nucleotides_problem at Rosalind", + "source_url": "https://rosalind.info/problems/dna/" +} diff --git a/exercises/practice/nucleotide-count/.meta/hints.md b/exercises/practice/nucleotide-count/.meta/hints.md new file mode 100644 index 0000000..ad0f5a1 --- /dev/null +++ b/exercises/practice/nucleotide-count/.meta/hints.md @@ -0,0 +1,3 @@ +# Hints + + diff --git a/exercises/practice/nucleotide-count/.meta/tests.toml b/exercises/practice/nucleotide-count/.meta/tests.toml new file mode 100644 index 0000000..7c55e53 --- /dev/null +++ b/exercises/practice/nucleotide-count/.meta/tests.toml @@ -0,0 +1,25 @@ +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. + +[3e5c30a8-87e2-4845-a815-a49671ade970] +description = "empty strand" + +[a0ea42a6-06d9-4ac6-828c-7ccaccf98fec] +description = "can count one nucleotide in single-character input" + +[eca0d565-ed8c-43e7-9033-6cefbf5115b5] +description = "strand with repeated nucleotide" + +[40a45eac-c83f-4740-901a-20b22d15a39f] +description = "strand with multiple nucleotides" + +[b4c47851-ee9e-4b0a-be70-a86e343bd851] +description = "strand with invalid nucleotides" diff --git a/exercises/practice/nucleotide-count/NucleotideCount.cfc b/exercises/practice/nucleotide-count/NucleotideCount.cfc new file mode 100644 index 0000000..375e488 --- /dev/null +++ b/exercises/practice/nucleotide-count/NucleotideCount.cfc @@ -0,0 +1,32 @@ +/** +* Your implementation of the NucleotideCount exercise +*/ +component { + + /** + * @returns + */ + function nucleotideCounts( strand ) { + a = 0 + c = 0 + g = 0 + t = 0 + cfloop(index="i" from="1" to="#len(strand)#") { + char = strand[i] + if (char == "a") { + a += 1 + } else if (char == "c") { + c += 1 + } else if (char == "g") { + g += 1 + } else if (char == "t") { + t += 1 + } else { + Throw(message='Invalid nucleotide in strand') + } + } + + return {"A":a,"C":c,"G":g,"T":t} + } + +} \ No newline at end of file diff --git a/exercises/practice/nucleotide-count/NucleotideCountTest.cfc b/exercises/practice/nucleotide-count/NucleotideCountTest.cfc new file mode 100644 index 0000000..7a938cb --- /dev/null +++ b/exercises/practice/nucleotide-count/NucleotideCountTest.cfc @@ -0,0 +1,35 @@ +component extends="testbox.system.BaseSpec" { + + function beforeAll(){ + SUT = createObject( 'NucleotideCount' ); + } + + function run(){ + + describe( "My NucleotideCount class", function(){ + + it( 'empty strand', function(){ + expect( SUT.nucleotideCounts( strand='' ) ).toBe( {"A":0,"C":0,"G":0,"T":0} ); + }); + + it( 'can count one nucleotide in single-character input', function(){ + expect( SUT.nucleotideCounts( strand='G' ) ).toBe( {"A":0,"C":0,"G":1,"T":0} ); + }); + + it( 'strand with repeated nucleotide', function(){ + expect( SUT.nucleotideCounts( strand='GGGGGGG' ) ).toBe( {"A":0,"C":0,"G":7,"T":0} ); + }); + + it( 'strand with multiple nucleotides', function(){ + expect( SUT.nucleotideCounts( strand='AGCTTTTCATTCTGACTGCAACGGGCAATATGTCTCTGTGTGGATTAAAAAAAGAGTGTCTGATAGCAGC' ) ).toBe( {"A":20,"C":12,"G":17,"T":21} ); + }); + + it( 'strand with invalid nucleotides', function(){ + expect( function(){ SUT.nucleotideCounts( strand='AGXXACT' ); } ).toThrow( message='Invalid nucleotide in strand' ); + }); + + }); + + } + +} \ No newline at end of file diff --git a/exercises/practice/nucleotide-count/TestRunner.cfc b/exercises/practice/nucleotide-count/TestRunner.cfc new file mode 100644 index 0000000..762b153 --- /dev/null +++ b/exercises/practice/nucleotide-count/TestRunner.cfc @@ -0,0 +1,103 @@ +/** +* I am a CommandBox task runner which you can use to test your implementation of this exercise against the +* provided test suite. To use me, open the CommandBox CLI and run this: +* +* CommandBox> task run TestRunner +* +* To start up a test watcher that will automatically rerun the test suite every time you save a file change, run this: +* +* CommandBox> task run TestRunner --watcher +* +*/ +component { + + /** + * @solution Runs the tests against the solution + * @watcher Start up a file watch that re-runs the tests on file changes. Use Ctrl-C to stop + */ + function run( boolean solution=false, boolean watcher=false ) { + + ensureTestBox(); + + if( watcher ) { + + // Tabula rasa + command( 'cls' ).run(); + + // Start watcher + watch() + .paths( '*.cfc' ) + .inDirectory( getCWD() ) + .withDelay( 500 ) + .onChange( function() { + + // Clear the screen + command( 'cls' ) + .run(); + + // This is neccessary so changes to tests get picked up right away. + pagePoolClear(); + + runTests( solution ); + + } ) + .start(); + + } else { + runTests( solution ); + } + + } + + /** + * Make sure the TestBox framework is installed + */ + private function ensureTestBox() { + var excerciseRoot = getCWD(); + var testBoxRoot = excerciseRoot & '/testbox'; + + if( !directoryExists( testBoxRoot ) ) { + + print.boldYellowLine( 'Installing some missing dependencies for you!' ).toConsole(); + command( 'install' ) + .inWorkingDirectory( excerciseRoot ) + .run(); + } + + // Bootstrap TestBox framework + filesystemUtil.createMapping( '/testbox', testBoxRoot ); + } + + /** + * Invoke TestBox to run the test suite + */ + private function runTests( boolean solution=false ) { + + // Create TestBox and run the tests + testData = new testbox.system.TestBox() + .runRaw( directory = { + // Find all CFCs... + mapping = filesystemUtil.makePathRelative( getCWD() ), + // ... in this directory ... + recurse = false, + // ... whose name ends in "test" + filter = function( path ) { + return path.reFind( ( solution ? 'Solution' : '' ) & 'Test.cfc$' ); + } + } ) + .getMemento(); + + // Print out the results with ANSI formatting for the CLI + getInstance( 'CLIRenderer@testbox-commands' ) + .render( print, testData, true ); + + print.toConsole(); + + // Set proper exit code + if( testData.totalFail || testData.totalError ) { + setExitCode( 1 ); + } + } + +} + diff --git a/exercises/practice/nucleotide-count/box.json b/exercises/practice/nucleotide-count/box.json new file mode 100644 index 0000000..3b95f7e --- /dev/null +++ b/exercises/practice/nucleotide-count/box.json @@ -0,0 +1,8 @@ +{ + "dependencies":{ + "testbox":"^2.5.0+107" + }, + "installPaths":{ + "testbox":"testbox/" + } +} \ No newline at end of file diff --git a/exercises/practice/nucleotide-count/index.cfm b/exercises/practice/nucleotide-count/index.cfm new file mode 100644 index 0000000..1e35079 --- /dev/null +++ b/exercises/practice/nucleotide-count/index.cfm @@ -0,0 +1,37 @@ + + + + + // get a list of all CFCs in this folder whose name looks like "XXXTest.cfc" + // And turn it into compnent path relative to the web root + url.bundles = directoryList( + path=expandPath( '/' ), + filter='*Test.cfc' ) + .map( function( path ) { + return path + .replaceNoCase( expandPath( '/' ), '' ) + .left( -4 ) + } ) + .toList(); + + + + + + + + Oops, you don't have TestBox installed yet! Please run box install from the root of your excercise folder and refresh this page. + From ce1d0ea26fb1a570f4fc32fba433117ceae0a1a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A1s=20B=20Nagy?= <20251272+BNAndras@users.noreply.github.com> Date: Sat, 11 Nov 2023 22:18:30 -0800 Subject: [PATCH 2/3] Delete unnneeded hints file --- exercises/practice/nucleotide-count/.meta/hints.md | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 exercises/practice/nucleotide-count/.meta/hints.md diff --git a/exercises/practice/nucleotide-count/.meta/hints.md b/exercises/practice/nucleotide-count/.meta/hints.md deleted file mode 100644 index ad0f5a1..0000000 --- a/exercises/practice/nucleotide-count/.meta/hints.md +++ /dev/null @@ -1,3 +0,0 @@ -# Hints - - From 5eadc27c838a6bc40e5ab5329f09dc0a5a83e69a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A1s=20B=20Nagy?= <20251272+BNAndras@users.noreply.github.com> Date: Tue, 14 Nov 2023 09:01:46 -0800 Subject: [PATCH 3/3] Swap example and stub --- .../nucleotide-count/.meta/Example.cfc | 28 ++++++++++++++++++- .../nucleotide-count/NucleotideCount.cfc | 23 ++------------- 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/exercises/practice/nucleotide-count/.meta/Example.cfc b/exercises/practice/nucleotide-count/.meta/Example.cfc index e26d452..3cc739b 100644 --- a/exercises/practice/nucleotide-count/.meta/Example.cfc +++ b/exercises/practice/nucleotide-count/.meta/Example.cfc @@ -2,5 +2,31 @@ * Here is an example solution for the NucleotideCount exercise */ component { - + + /** + * @returns + */ + function nucleotideCounts( strand ) { + a = 0 + c = 0 + g = 0 + t = 0 + cfloop(index="i" from="1" to="#len(strand)#") { + char = strand[i] + if (char == "a") { + a += 1 + } else if (char == "c") { + c += 1 + } else if (char == "g") { + g += 1 + } else if (char == "t") { + t += 1 + } else { + Throw(message='Invalid nucleotide in strand') + } + } + + return {"A":a,"C":c,"G":g,"T":t} + } + } \ No newline at end of file diff --git a/exercises/practice/nucleotide-count/NucleotideCount.cfc b/exercises/practice/nucleotide-count/NucleotideCount.cfc index 375e488..58ec30e 100644 --- a/exercises/practice/nucleotide-count/NucleotideCount.cfc +++ b/exercises/practice/nucleotide-count/NucleotideCount.cfc @@ -7,26 +7,7 @@ component { * @returns */ function nucleotideCounts( strand ) { - a = 0 - c = 0 - g = 0 - t = 0 - cfloop(index="i" from="1" to="#len(strand)#") { - char = strand[i] - if (char == "a") { - a += 1 - } else if (char == "c") { - c += 1 - } else if (char == "g") { - g += 1 - } else if (char == "t") { - t += 1 - } else { - Throw(message='Invalid nucleotide in strand') - } - } - - return {"A":a,"C":c,"G":g,"T":t} + // Implement me here } -} \ No newline at end of file +}