Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Building a training set of tags for cpp #734

Closed
ErikSchierboom opened this issue Oct 31, 2023 · 26 comments
Closed

Building a training set of tags for cpp #734

ErikSchierboom opened this issue Oct 31, 2023 · 26 comments

Comments

@ErikSchierboom
Copy link
Member

Hello lovely maintainers 👋

We've recently added "tags" to student's solutions. These express the constructs, paradigms and techniques that a solution uses. We are going to be using these tags for lots of things including filtering, pointing a student to alternative approaches, and much more.

In order to do this, we've built out a full AST-based tagger in C#, which has allowed us to do things like detect recursion or bit shifting. We've set things up so other tracks can do the same for their languages, but its a lot of work, and we've determined that actually it may be unnecessary. Instead we think that we can use machine learning to achieve tagging with good enough results. We've fine-tuned a model that can determine the correct tags for C# from the examples with a high success rate. It's also doing reasonably well in an untrained state for other languages. We think that with only a few examples per language, we can potentially get some quite good results, and that we can then refine things further as we go.

I released a new video on the Insiders page that talks through this in more detail.

We're going to be adding a fully-fledged UI in the coming weeks that allow maintainers and mentors to tag solutions and create training sets for the neural networks, but to start with, we're hoping you would be willing to manually tag 20 solutions for this track. In this post we'll add 20 comments, each with a student's solution, and the tags our model has generated. Your mission (should you choose to accept it) is to edit the tags on each issue, removing any incorrect ones, and add any that are missing. In order to build one model that performs well across languages, it's best if you stick as closely as possible to the C# tags as you can. Those are listed here. If you want to add extra tags, that's totally fine, but please don't arbitrarily reword existing tags, even if you don't like what Erik's chosen, as it'll just make it less likely that your language gets the correct tags assigned by the neural network.


To summarise - there are two paths forward for this issue:

  1. You're up for helping: Add a comment saying you're up for helping. Update the tags some time in the next few days. Add a comment when you're done. We'll then add them to our training set and move forward.
  2. You not up for helping: No problem! Just please add a comment letting us know :)

If you tell us you're not able/wanting to help or there's no comment added, we'll automatically crowd-source this in a week or so.

Finally, if you have questions or want to discuss things, it would be best done on the forum, so the knowledge can be shared across all maintainers in all tracks.

Thanks for your help! 💙


Note: Meta discussion on the forum

@ErikSchierboom
Copy link
Member Author

ErikSchierboom commented Oct 31, 2023

Exercise: hello-world

Code

#if !defined(HELLO_WORLD_H)
#define HELLO_WORLD_H

#include <string>

namespace hello_world {

std::string hello() {
  return "Hello, World!";
}

}

#endif

Tags:

construct:define
construct:header
construct:namespace
construct:return
construct:string
construct:verbatim
paradigm:declarative
paradigm:imperative

@ErikSchierboom
Copy link
Member Author

ErikSchierboom commented Oct 31, 2023

Exercise: word-count

Code

#ifndef __WORD_COUNT_H__
#define __WORD_COUNT_H__

#include <map>
#include <string>
#include <sstream>
#include <algorithm>
#include <vector>
#include <iostream>

namespace word_count
{
	std::map<std::string, int> words(const std::string &input)
	{
		std::map<std::string, int> result;
		std::string sanitised;

		for(char inputCh : input)
		{
			if (std::isalnum(inputCh) || std::iswspace(inputCh))
			{
				sanitised += tolower(inputCh);
			}
			else if (inputCh == ',')
			{
				sanitised += ' ';
			}
			else if(inputCh == '\'')
			{
				sanitised += '\'';
			}
		}

		std::istringstream iss(sanitised);
		std::vector<std::string> tokens;
    	copy(std::istream_iterator<std::string>(iss), std::istream_iterator<std::string>(), back_inserter(tokens));
    	for (std::string token : tokens)
    	{
    		if (std::find_if_not(token.begin(), token.end(), std::not1(std::ptr_fun<int, int>(std::isalnum))) == token.end())
    		{
    			continue;
    		}

    		if (token[0] == '\'')
    		{
    			token.erase(token.begin(), token.begin()+1);
    		}

			if (token[token.length()-1] == '\'')
    		{
    			token.erase(token.end()-1, token.end());
			}

    		auto search = result.find(token);

    		if (search == result.end())
    		{
    			result[token] = 1;
    		}
    		else
    		{
    			result[token] = result[token] + 1;
    		}
    	}
		return result;
	}
}

#endif

Tags:

construct:add
construct:assignment
construct:back_inserter
construct:char
construct:continue
construct:define
construct:for-loop
construct:if
construct:index-operator
construct:int
construct:integral-number
construct:invocation
construct:iterator
construct:map
construct:method
construct:namespace
construct:number
construct:parameter
construct:return
construct:string
construct:subtract
construct:variable
construct:vector
paradigm:imperative
paradigm:object-oriented
technique:higher-order-functions
technique:looping
uses:map
uses:vector

@ErikSchierboom
Copy link
Member Author

ErikSchierboom commented Oct 31, 2023

Exercise: food-chain

Code

#include "food_chain.h"

#ifndef __FOOD_CHAIN_H__
#define __FOOD_CHAIN_H__

#include <string>
using namespace std;

namespace food_chain {
	string verse(int num);
	string verses(int start, int end);
	string sing();
}

#endif

#include <sstream>

const string spider_special(" that wriggled and jiggled and tickled inside her");
const string animals[] = { "fly", "spider", "bird", "cat", "dog", "goat", "cow", "horse"  };
const string sentence2[] = {
	"It wriggled and jiggled and tickled inside her.\n",
	"How absurd to swallow a bird!\n",
	"Imagine that, to swallow a cat!\n",
	"What a hog, to swallow a dog!\n",
	"Just opened her throat and swallowed a goat!\n",
	"I don't know how she swallowed a cow!\n"
};

string food_chain::verse(int num) {
	stringstream out;
	out << "I know an old lady who swallowed a " << animals[num - 1] << ".\n";
	if (num >= 8) {
		out << "She's dead, of course!\n";
		return out.str();
	}
	if (num > 1)
		out << sentence2[num - 2];
	for (int ix = num; ix > 1; ix--) {
		out << "She swallowed the " 
			<< animals[ix - 1] 
			<< " to catch the " 
			<< animals[ix - 2] 
			<< (ix == 3 ? spider_special : "")
			<< ".\n";
	}
	out << "I don't know why she swallowed the fly. Perhaps she'll die.\n";

	return out.str();
}

string food_chain::verses(int start, int end) {
	stringstream out;
	for (int ix = start; ix <= end; ix++) {
		out << verse(ix) << "\n";
	}
	return out.str();
}

string food_chain::sing() {
	return verses(1, 8);
}

Tags:

construct:for-loop
construct:if
construct:index
construct:int
construct:integral-number
construct:invocation
construct:method
construct:namespace
construct:number
construct:parameter
construct:return
construct:string
construct:subtract
construct:ternary
construct:using-directive
construct:variable
paradigm:object-oriented
technique:looping

@ErikSchierboom
Copy link
Member Author

ErikSchierboom commented Oct 31, 2023

Exercise: grade-school

Code

using namespace std;
#include <string>
#include <map>
#include <vector>
#include <set>
#include <iostream>

// I'm not very familiar with TDD, so this may be a naive comment.
// The tests expect a vector back from all the calls that return something.
// But they also expect them to be sorted.
// Why, then, don't they expect a set?  It seems like that would be a reasonable
// negotiation when the API was created in the first place (in the hypothetical company
// where this software is being built)
//
// Of course, this is only an exercise, so expecting a sorted vector might just be part of the fun.
//
namespace grade_school
{
    
    typedef map<int, vector<string>> rosterMap;
    
    class school
    {
    public:
        school()
        {
            theRoster = {};
        }
        
        void add(string pupil, int grade)
        {
            auto &pupilList = theRoster[grade];
            
            // The pupil list has to be kept sorted.
            // It'd be nice to use a set, but the tests want a vector
            // So we build a vector and keep it sorted
            if (pupilList.empty())  // Empty list, doesn't matter
            {
                pupilList.push_back(pupil);
                return;
            }
            
            // Does it need to go at the beginning or end?
            if (pupil < pupilList.front())
            {
                pupilList.insert(pupilList.begin(), pupil);
            }
            else if (pupil > pupilList.back())
            {
                pupilList.push_back(pupil);
            }
            else
            {
                // Goes somewhere in the middle
                // Find where to add the name
                for (auto entry = pupilList.begin() + 1 ; entry != pupilList.end() ; entry++)
                {
                    if (pupil > *(entry - 1) && pupil < *entry)
                    {
                        pupilList.insert(entry, pupil);
                        break;
                    }
                }
            }
        }
        
        const vector<string> grade(int whatGrade)
        {
            vector<string> theList(theRoster[whatGrade]);
            
            sort(theList.begin(), theList.end());
            return theList;
        }
        
        rosterMap roster()
        { 
            return theRoster;
        };
        
#ifdef DEBUG
        // For debugging
        void dumpRoster()
        {
            for (auto gradeRoster : theRoster)
            {
                cout << "Grade: " << gradeRoster.first << endl;
                for (auto pupil : gradeRoster.second)
                    cout << "\t" << pupil << endl;
            }
            cout << "---------------" << endl;
        }
#endif       
    private:
        rosterMap theRoster;
    };
};

Tags:

construct:add
construct:assignment
construct:break
construct:class
construct:comment
construct:constructor
construct:for-loop
construct:if
construct:include
construct:int
construct:integral-number
construct:invocation
construct:logical-and
construct:map
construct:method
construct:namespace
construct:number
construct:parameter
construct:return
construct:string
construct:subtract
construct:typedef
construct:using-directive
construct:variable
construct:vector
paradigm:imperative
paradigm:object-oriented
technique:boolean-logic
technique:looping
uses:map
uses:vector

@ErikSchierboom
Copy link
Member Author

ErikSchierboom commented Oct 31, 2023

Exercise: robot-name

Code

#pragma once

#include <iomanip>
#include <sstream>
#include <string>

namespace robot_name {
    class name {
        public:
            name(const std::string& model, const int number) : model(model), number(number) {}

            // returns old name while incrementing to the new name
            // Similar to how `return x++` returns the old value of `x` while incrementing `x`
            robot_name::name increment() {
                robot_name::name previous = *this;

                if (number == 999) {
                    number = 0;

                    if (model[1] == 'Z') {
                        model[1] = 'A';

                        if (model[0] == 'Z') {
                            model[0] = 'A';
                        } else {
                            model[0]++;
                        }
                    } else {
                        model[1]++;
                    }
                } else {
                    number ++;
                }

                return previous;
            }

            operator std::string() const {
                std::ostringstream stream;
                stream << this->model << std::setw(3) << std::setfill('0') << this->number;
                return stream.str();
            }

        private:
            std::string model;
            int number;
    };

    class robot {
        public:
            robot() {
                this->reset();
            }

            std::string name() const {
                return this->label;
            }

            void reset() {
                label = std::string(robot::next_name.increment());
            }

        private:
            static robot_name::name next_name;
            std::string label;
    };

    robot_name::name robot::next_name("AA", 0);
};

Tags:

construct:assignment
construct:char
construct:class
construct:comment
construct:constructor
construct:field
construct:if
construct:index-operator
construct:int
construct:integral-number
construct:invocation
construct:method
construct:namespace
construct:number
construct:parameter
construct:pragma
construct:variable
construct:visibility-modifiers
paradigm:imperative
paradigm:object-oriented

@ErikSchierboom
Copy link
Member Author

ErikSchierboom commented Oct 31, 2023

Exercise: scrabble-score

Code

#pragma once

#include <cctype>
#include <string>

using std::string;

class scrabble_score {
  public:
    static int score(const string &str) {
        static constexpr int scores[] = { 1, 3, 3, 2, 1, 4, 2, 4, 1, 8,
                                          5, 1, 3, 1, 1, 3, 10, 1, 1, 1,
                                          1, 4, 4, 8, 4, 10 };
        int sum = 0;
        for (char ch : str) {
            sum += scores[tolower(ch) - 'a'];
        }
        return sum;
    }
};

Tags:

construct:class
construct:method
construct:parameter
construct:using-directive
construct:visibility-modifiers
paradigm:object-oriented

@ErikSchierboom
Copy link
Member Author

ErikSchierboom commented Oct 31, 2023

Exercise: grains

Code

/*
  exercism.io C++ track: grains
  Robert Wheeler
*/

#ifndef GRAINS_H
#define GRAINS_H

#include <unordered_map>

namespace grains
{

  const std::unordered_map< int, unsigned long long > powersOfTwo {
                                        {0,                        1 },
                                        {1,                        2 },
                                        {2,                        4 },
                                        {3,                        8 },
                                        {4,                       16 },
                                        {5,                       32 },
                                        {6,                       64 },
                                        {7,                      128 },
                                        {8,                      256 },
                                        {9,                      512 },
                                        {10,                    1024 },
                                        {11,                    2048 },
                                        {12,                    4096 },
                                        {13,                    8192 },
                                        {14,                   16384 },
                                        {15,                   32768 },
                                        {16,                   65536 },
                                        {17,                  131072 },
                                        {18,                  262144 },
                                        {19,                  524288 },
                                        {20,                 1048576 },
                                        {21,                 2097152 },
                                        {22,                 4194304 },
                                        {23,                 8388608 },
                                        {24,                16777216 },
                                        {25,                33554432 },
                                        {26,                67108864 },
                                        {27,               134217728 },
                                        {28,               268435456 },
                                        {29,               536870912 },
                                        {30,              1073741824 },
                                        {31,              2147483648 },
                                        {32,              4294967296 },
                                        {33,              8589934592 },
                                        {34,             17179869184 },
                                        {35,             34359738368 },
                                        {36,             68719476736 },
                                        {37,            137438953472 },
                                        {38,            274877906944 },
                                        {39,            549755813888 },
                                        {40,           1099511627776 },
                                        {41,           2199023255552 },
                                        {42,           4398046511104 },
                                        {43,           8796093022208 },
                                        {44,          17592186044416 },
                                        {45,          35184372088832 },
                                        {46,          70368744177664 },
                                        {47,         140737488355328 },
                                        {48,         281474976710656 },
                                        {49,         562949953421312 },
                                        {50,        1125899906842624 },
                                        {51,        2251799813685248 },
                                        {52,        4503599627370496 },
                                        {53,        9007199254740992 },
                                        {54,       18014398509481984 },
                                        {55,       36028797018963968 },
                                        {56,       72057594037927936 },
                                        {57,      144115188075855872 },
                                        {58,      288230376151711744 },
                                        {59,      576460752303423488 },
                                        {60,     1152921504606846976 },
                                        {61,     2305843009213693952 },
                                        {62,     4611686018427387904 },
                                        {63,  9223372036854775808ULL },
                                      };

  const unsigned long long grainTotal = 18446744073709551615ULL;

  unsigned long long square( int sq ) {
    return grains::powersOfTwo.at( sq-1 ); };

  unsigned long long total() {
    return grains::grainTotal; };

}

#endif //GRAINS_H

Tags:

construct:comment
construct:const
construct:initializer-list
construct:int
construct:integral-number
construct:invocation
construct:long-long
construct:map
construct:method
construct:namespace
construct:number
construct:parameter
construct:return
construct:subtract
construct:unordered-map
construct:using-directive
construct:variable
construct:visibility-modifiers
paradigm:object-oriented
uses:std.map

@ErikSchierboom
Copy link
Member Author

ErikSchierboom commented Oct 31, 2023

Exercise: raindrops

Code

#include <string>

namespace raindrops {
  std::string convert(unsigned n);
}

// Implementation

#include <map>
#include <sstream>
#include <vector>

using namespace std;

string raindrops::convert(unsigned drops) {
  stringstream stream;

  for (auto &&e: map<unsigned, string> {
                   { 3, "Pling" }, { 5, "Plang" }, { 7, "Plong" }
                 }) if (not (drops % e.first)) stream << e.second;

  if (not stream.tellp()) stream << drops;

  return stream.str();
}

Tags:

construct:auto
construct:comment
construct:for-loop
construct:if
construct:initializer-list
construct:integral-number
construct:invocation
construct:logical-not
construct:map
construct:method
construct:namespace
construct:number
construct:parameter
construct:return
construct:string
construct:template
construct:unsigned-integral
construct:using-directive
construct:variable
paradigm:imperative
uses:map
uses:string

@ErikSchierboom
Copy link
Member Author

ErikSchierboom commented Oct 31, 2023

Exercise: trinary

Code

#pragma once

#include <cmath>
#include <string>

namespace trinary {
    const int to_decimal(const std::string& n) {
        int output = 0;
        const std::reverse_iterator<std::string::iterator> r = std::string(n).rbegin();

        for (int i = 0; i < n.size(); i++) {
            const int digit = r[i] - '0';

            if (1 <= digit && digit <= 2) {
                output += digit * pow(3, i);
            }
        }

        return output;
    }
};

Tags:

construct:assignment
construct:boolean
construct:char
construct:const
construct:for-loop
construct:if
construct:index-operator
construct:int
construct:integral-number
construct:invocation
construct:logical-and
construct:method
construct:multiply
construct:namespace
construct:number
construct:parameter
construct:pragma
construct:return
construct:string
construct:subtract
construct:variable
paradigm:imperative
technique:looping

@ErikSchierboom
Copy link
Member Author

ErikSchierboom commented Oct 31, 2023

Exercise: say

Code

#ifndef _SAY_H
#define _SAY_H

#include <cmath>
#include <map>
#include <sstream>
#include <string>
#include <stdexcept>

namespace say {

std::map<unsigned int, std::string> units = {{0, "zero"}, {1, "one"}, {2, "two"},
													{3, "three"}, {4, "four"}, {5, "five"},
													{6, "six"}, {7, "seven"}, {8, "eight"},
													{9, "nine"}};

std::map<unsigned int, std::string> tens = {{10, "ten"}, {20, "twenty"}, {30, "thirty"},
													{40, "forty"}, {50, "fifty"}, {60, "sixty"},
													{70, "seventy"}, {80, "eighty"}, {90, "ninety"}};

std::map<unsigned int, std::string> teens = {{11, "eleven"}, {12, "twelve"}, {13, "thirteen"},
													{14, "fourteen"}, {15, "fifteen"}, {16, "sixteen"},
													{17, "seventeen"}, {18, "eighteen"}, {19, "nineteen"}};

/*note: leading space included to avoid handling it in code later for special case of 1 (no name)*/
std::map<unsigned int, std::string> names = {{1000000000, " billion"}, {1000000, " million"},
													{1000, " thousand"}, {1, ""}};

/*english translation of numbers between 0 and 99*/
std::string transform_0_99 (unsigned int number) {
	if(number < 0 || number > 99)
		throw std::domain_error("Out of range");
	
	if(number < 10)
		return units[number];
	
	unsigned int rem = number % 10;
	
	if(rem == 0)
		return tens[number];
	
	if(number < 20)
		return teens[number];
	
	return tens[number - rem] + "-" + units[rem];
}

/*english translation of numbers between 0 and 999*/
std::string transform3digit(unsigned int number) {
	if(number < 0 || number > 999)
		throw std::domain_error("Out of range");
	
	if(number < 100)
		return transform_0_99(number);
	
	if(number % 100 == 0)
		return units[number / 100] + " hundred";
	
	return units[number / 100] + " hundred " + transform_0_99(number - (number / 100) * 100);
}

/*english translation of numbers between 0 and 999 999 999 999*/
std::string in_english(long long number) {
	if(number < 0 || number > 999999999999)
		throw std::domain_error("Out of range");
	
	if(number == 0)
		return "zero";
	
	std::ostringstream o;
	
	/* tracks whether a leading space is needed before the translation of a group of numbers
	 * which is the case if it not the first non-zero group */
	bool first = true;
	
	/*holds the current 3 digit multiplier for a given magnitude*/
	long long tmp = number;
	
	for(int i = 3; i >= 0; i--) {
		unsigned long long power = std::pow(10, i * 3);
		tmp = number / power;
		if(tmp > 0) {
			if(first == true)
				first = false;
			else
				o << " ";
			o <<  transform3digit(tmp) << names[power];
			number -= tmp * power;
		}
	}

	return o.str();
}
	
} /*namespace say*/

#endif

Tags:

construct:add
construct:assignment
construct:boolean
construct:comment
construct:constructor
construct:divide
construct:double
construct:double-precision:floating-point-number
construct:for-loop
construct:if
construct:initializer-list
construct:int
construct:integral-number
construct:invocation
construct:logical-or
construct:long
construct:long-double
construct:long-long-int
construct:map
construct:multiply
construct:namespace
construct:number
construct:parameter
construct:return
construct:string
construct:subtract
construct:throw
construct:true
construct:variable
paradigm:imperative
technique:boolean-logic
technique:exceptions
technique:looping
technique:math

@ErikSchierboom
Copy link
Member Author

ErikSchierboom commented Oct 31, 2023

Exercise: sieve

Code

#include <vector>

namespace sieve { std::vector<int> primes(unsigned); }

// Implementation

#include <algorithm>

using namespace std;

vector<int> sieve::primes(unsigned max) {
  vector<int> sieve;
  sieve.reserve(max/2);
  if (max >= 2) {
    sieve.push_back(2);
    for (unsigned i = 3; i <= max; i += 2) sieve.push_back(i);
  }

  auto prime = sieve.empty()? begin(sieve): next(begin(sieve));
  auto eod = end(sieve);
  while (prime != eod and *prime * *prime < max) {
    eod = remove_if(prime + 1, eod,
                    [&prime](int n) { return not (n % *prime); });
    ++prime;
  }
  sieve.erase(eod, end(sieve));

  return sieve;
}

Tags:

construct:add
construct:algorithm
construct:assignment
construct:auto
construct:boolean
construct:comment
construct:divide
construct:for-loop
construct:function
construct:if
construct:int
construct:integral-number
construct:invocation
construct:lambda
construct:logical-and
construct:method
construct:multiply
construct:namespace
construct:number
construct:parameter
construct:return
construct:subtract
construct:ternary
construct:typedef
construct:using-directive
construct:variable
construct:vector
construct:while-loop
paradigm:functional
paradigm:imperative
technique:boolean-logic
technique:higher-order-functions
technique:looping
uses:vector

@ErikSchierboom
Copy link
Member Author

ErikSchierboom commented Oct 31, 2023

Exercise: queen-attack

Code

#ifndef _QUEEN_ATTACK_H
#define _QUEEN_ATTACK_H

#include <sstream>
#include <stdexcept>
#include <string>
#include <utility>

namespace queen_attack {

typedef std::pair<int, int> position;
	
class chess_board {
public:
	chess_board();
	chess_board(position white, position black);
	position white() const;
	position black() const;
	bool can_attack() const;
	explicit operator std::string() const;
	
private:
	int _board[8][8];
	position _white;
	position _black;
};

chess_board::chess_board() {
	_white = {0, 3};
	_black = {7, 3};
}

chess_board::chess_board(position white, position black) {
	if(white == black){
		throw std::domain_error("Invalid positions");
	}
	_white = white;
	_black = black;
}

bool chess_board::can_attack() const {
	/* row or column attack */
	if(_white.first == _black.first || _white.second == _black.second)
		return true;
	
	/* diagonal attack */
	if(std::abs(_white.first - _black.first) == std::abs(_white.second - _black.second))
		return true;
		
	return false;
}

position chess_board::black() const {
	return _black;
}

position chess_board::white() const {
	return _white;
}

chess_board::operator std::string() const {
	std::ostringstream o;
	for(int i = 0; i < 8; i++) {
		for(int j = 0; j < 8; j++) {
			if(_white.first == i && _white.second == j)
				o << 'W';
			else if(_black.first == i && _black.second == j)
				o << 'B';
			else
				o << '_';
			
			if(j != 7)
				o << ' ';
		}
		o << std::endl;
	}
	return o.str();
}

}

#endif

Tags:

construct:assignment
construct:boolean
construct:char
construct:class
construct:comment
construct:constructor
construct:field
construct:for-loop
construct:if
construct:initializer-list
construct:int
construct:integral-number
construct:logical-and
construct:logical-or
construct:method
construct:namespace
construct:number
construct:parameter
construct:return
construct:string
construct:subtract
construct:typedef
construct:throw
construct:variable
construct:visibility-modifiers
paradigm:imperative
paradigm:object-oriented
technique:boolean-logic
technique:exceptions
technique:looping

@ErikSchierboom
Copy link
Member Author

ErikSchierboom commented Oct 31, 2023

Exercise: atbash-cipher

Code

#include <algorithm>
#include <string>

namespace atbash {
  using namespace std;

  char enc (char c) {
    c = tolower(c);
    if (c >= 'a' && c <= 'z') {
      return 'z' - (c - 'a');
    }
    return c;
  }

  char dec (char c) {
    if (c >= 'a' && c <= 'z') {
      return 'a' + ('z' - c);
    }
    return c;
  }

  string encode (string plain) {
    plain.erase(remove_if(plain.begin(), plain.end(), [](char c) {
          return c == ' ' || c == ',' || c == '.'; }), plain.end());
    string encoded;
    transform(plain.begin(), plain.end(), back_inserter(encoded), enc);
    string spaced;
    for (int i = 0; i < encoded.size(); ++i) {
      spaced += encoded[i];
      if (i > 0 && i != encoded.size()-1 && (i+1) % 5 == 0) {
        spaced += ' ';
      }
    }

    return spaced;
  }

  string decode (string encoded) {
    encoded.erase(remove_if(encoded.begin(), encoded.end(), [](char c) {
          return c == ' '; }), encoded.end());
    string decoded;
    transform(encoded.begin(), encoded.end(), back_inserter(decoded), dec);
    return decoded;
  }
}

#define EXERCISM_RUN_ALL_TESTS

Tags:

construct:add
construct:assignment
construct:boolean
construct:char
construct:for-loop
construct:function
construct:if
construct:index
construct:int
construct:integral-number
construct:invocation
construct:lambda
construct:logical-and
construct:logical-or
construct:method
construct:namespace
construct:number
construct:parameter
construct:return
construct:string
construct:subtract
construct:transform
construct:using-directive
construct:variable
paradigm:functional
paradigm:imperative
technique:boolean-logic
technique:higher-order-functions
technique:looping

@ErikSchierboom
Copy link
Member Author

ErikSchierboom commented Oct 31, 2023

Exercise: all-your-base

Code

#if !defined(ALL_YOUR_BASE_H)
#define ALL_YOUR_BASE_H

#include <vector>

namespace all_your_base {
	std::vector<unsigned int> convert(unsigned int from, std::vector<unsigned int> digits, unsigned int to) {
		if (from <= 1 || to <= 1)
			return {};

		if (digits.size() > 1 && digits[0] == 0)
			return {};
		
		unsigned int num = 0;
		for (unsigned int i : digits) {
			if (i >= from)
				return {};
			num = num * from + i;
		}
		
		std::vector<unsigned int> ans;
		while (num) {
			unsigned d = num % to;
			ans.push_back( d );
			num /= to;
		}
		
		std::reverse(ans.begin(), ans.end());
		
		return ans;
	}
};

#endif

Tags:

construct:add
construct:assignment
construct:boolean
construct:for-loop
construct:if
construct:index
construct:initializer-list
construct:integral-number
construct:integral-type
construct:logical-and
construct:logical-or
construct:method
construct:multiply
construct:namespace
construct:number
construct:parameter
construct:return
construct:template
construct:unsigned-int
construct:variable
construct:vector
construct:while-loop
paradigm:imperative
technique:boolean-logic
technique:looping
uses:std::vector

@ErikSchierboom
Copy link
Member Author

ErikSchierboom commented Oct 31, 2023

Exercise: robot-simulator

Code

#ifndef ROBOT_SIMULATOR_H_
#define ROBOT_SIMULATOR_H_

#include <utility>
#include <string>

namespace robot_simulator {

enum class Bearing { NORTH, EAST, SOUTH, WEST };

Bearing& operator++(Bearing& bearing) {
    return bearing = static_cast<Bearing>(
        (static_cast<std::uint8_t>(bearing) + 1) % sizeof(Bearing));
}

Bearing& operator--(Bearing& bearing) {
    return bearing = static_cast<Bearing>(
        (static_cast<std::uint8_t>(bearing) - 1) % sizeof(Bearing));
}

class Robot {
    using position_type = std::pair<int, int>;
    using bearing_type = robot_simulator::Bearing;
public:
    Robot() : position_({0, 0}), bearing_(bearing_type::NORTH) {}

    explicit Robot(position_type position, bearing_type bearing) 
        : position_(position), bearing_(bearing) {}
    
    const position_type& get_position() const { return position_; }
    const bearing_type& get_bearing() const { return bearing_; }
    void turn_left() { --bearing_; }
    void turn_right() { ++bearing_; }
    void advance() {
        switch (bearing_) {
            case bearing_type::NORTH: ++position_.second; break;
            case bearing_type::EAST:  ++position_.first; break;
            case bearing_type::SOUTH: --position_.second; break;
            case bearing_type::WEST:  --position_.first; break;
        }
    }
    void execute_sequence(const std::string& sequence) {
        for (const unsigned char c : sequence) {
            switch (c) {
                case 'L': turn_left(); break;
                case 'R': turn_right(); break;
                case 'A': advance(); break;
            }
        }
    }
private:
    position_type position_;
    bearing_type bearing_;
};

} // namespace robot_simulator

#endif // !ROBOT_SIMULATOR_H_

Tags:

construct:assignment
construct:break
construct:char
construct:class
construct:constructor
construct:enum
construct:explicit-conversion
construct:for-loop
construct:initializer-list
construct:int
construct:integral-number
construct:invocation
construct:method
construct:namespace
construct:number
construct:parameter
construct:return
construct:string
construct:subtract
construct:switch
construct:typedef
construct:using-declaration
construct:variable
construct:visibility-modifiers
paradigm:imperative
paradigm:object-oriented
technique:looping
technique:type-conversion

@ErikSchierboom
Copy link
Member Author

ErikSchierboom commented Oct 31, 2023

Exercise: binary-search-tree

Code

#ifndef BINARY_SEARCH_TREE_H
#define BINARY_SEARCH_TREE_H

#include <memory>

namespace binary_tree {
  template <class T> class binary_tree;

  template <class T> using tree_ptr = std::unique_ptr<binary_tree<T>>;

  template <class T>
  class binary_tree {
  public:
    class inorder_iterator;

    explicit binary_tree(const T&);

    const T& data() const;
    const tree_ptr<T>& left() const;
    const tree_ptr<T>& right() const;
    inorder_iterator begin() const;
    inorder_iterator end() const;

    void insert(const T&);

  private:
    const T m_data;
    tree_ptr<T> m_left;
    tree_ptr<T> m_right;
    binary_tree* m_parent{nullptr};

    binary_tree(const T&, binary_tree*);
  };

  template <class T>
  binary_tree<T>::binary_tree(const T& data)
  : m_data{data} {}

  template <class T>
  const T& binary_tree<T>::data() const {
    return m_data;
  }

  template <class T>
  const tree_ptr<T>& binary_tree<T>::left() const {
    return m_left;
  }

  template <class T>
  const tree_ptr<T>& binary_tree<T>::right() const {
    return m_right;
  }

  template <class T>
  typename binary_tree<T>::inorder_iterator
  binary_tree<T>::begin() const {
    return inorder_iterator{this};
  }

  template <class T>
  typename binary_tree<T>::inorder_iterator
  binary_tree<T>::end() const {
    return inorder_iterator{};
  }

  template <class T>
  void binary_tree<T>::insert(const T& data) {
    auto* parent = this;
    auto* current = data <= m_data ? &m_left : &m_right;

    while (*current != nullptr) {
      parent =(*current).get();

      if (data <= (*current)->m_data)
        current = &(*current)->m_left;
      else
        current = &(*current)->m_right;
    }

    *current = std::unique_ptr<binary_tree<T>>{new binary_tree{data, parent}};
  }

  template <class T>
  binary_tree<T>::binary_tree(const T& data, binary_tree* parent)
  : m_data{data}, m_parent{parent} {}

  template <class T>
  class binary_tree<T>::inorder_iterator
  : public std::iterator<std::forward_iterator_tag, T, std::size_t, const T*, const T&> {
  public:
    explicit inorder_iterator(const binary_tree* = nullptr);

    inorder_iterator& operator++();
    const inorder_iterator operator++(int);
    bool operator==(const inorder_iterator&) const;
    bool operator!=(const inorder_iterator&) const;
    const T& operator*() const;

  private:
    const binary_tree<T>* m_current;

    const binary_tree* leftmost_child(const binary_tree*);
  };

  template <class T>
  binary_tree<T>::inorder_iterator::inorder_iterator(const binary_tree* root)
  : m_current{leftmost_child(root)} {}

  template <class T>
  typename binary_tree<T>::inorder_iterator&
  binary_tree<T>::inorder_iterator::operator++() {
    if (m_current->m_right != nullptr) {
      m_current = leftmost_child(m_current->m_right.get());
      return *this;
    }

    while (m_current != nullptr) {
      if (m_current->m_parent != nullptr && m_current->m_parent->m_left.get() == m_current) {
        m_current = m_current->m_parent;
        return *this;
      }

      m_current = m_current->m_parent;
    }

    return *this;
  }

  template <class T>
  const typename binary_tree<T>::inorder_iterator
  binary_tree<T>::inorder_iterator::operator++(int) {
    auto previous = *this;
    ++(*this);

    return previous;
  }

  template <class T>
  bool binary_tree<T>::inorder_iterator::operator==(const inorder_iterator& rhs) const {
    return m_current == rhs.m_current;
  }

  template <class T>
  bool binary_tree<T>::inorder_iterator::operator!=(const inorder_iterator& rhs) const {
    return !(*this == rhs);
  }

  template <class T>
  const T& binary_tree<T>::inorder_iterator::operator*() const {
    return m_current->m_data;
  }

  template <class T>
  const binary_tree<T>* binary_tree<T>::inorder_iterator::leftmost_child(const binary_tree* from) {
    if (from == nullptr)
      return from;

    while (from->m_left != nullptr)
      from = from->m_left.get();

    return from;
  }
}

#endif // BINARY_SEARCH_TREE_H

Tags:

construct:auto
construct:boolean
construct:class
construct:comment
construct:constructor
construct:const
construct:constructor-initializer
construct:field
construct:if
construct:initializer
construct:integral-number
construct:invocation
construct:method
construct:namespace
construct:null
construct:nullptr
construct:parameter
construct:ternary
construct:template
construct:template-alias
construct:template-template-parameter
construct:typedef
construct:using-declaration
construct:variable
construct:visibility-modifiers
construct:while-loop
paradigm:imperative
paradigm:object-oriented
paradigm:template-metaprogramming
technique:looping
uses:std::unique-ptr

@ErikSchierboom
Copy link
Member Author

ErikSchierboom commented Oct 31, 2023

Exercise: circular-buffer

Code

#ifndef CIRCULAR_BUFFER_H
#define CIRCULAR_BUFFER_H

#include <vector>

namespace circular_buffer {
template <class T>
class circular_buffer {
public:
  using size_type = typename std::vector<T>::size_type;
  using value_type = T;
    
  explicit circular_buffer(size_type size);
  value_type read();
  void write(const value_type& val);
  void overwrite(const value_type& val);
  void clear();
  
private:
  std::vector<T> m_data;
  size_type m_size {0};
  size_type m_read_pos {0};
  size_type m_write_pos {0};
    
  size_type next(size_type x) const;
};

template <class T>
circular_buffer<T>::circular_buffer(size_type size)
: m_data(size) {}

template <class T>
typename circular_buffer<T>::value_type
circular_buffer<T>::read() {
  if (m_size == 0)
    throw std::domain_error{"The buffer is empty."};
  
  const auto value = m_data[m_read_pos];
  m_read_pos = next(m_read_pos);
  --m_size;
  
  return value;
}

template <class T>
void
circular_buffer<T>::write(const value_type& val) {
  if (m_size == m_data.size())
    throw std::domain_error{"The buffer is full."};
  
  m_data[m_write_pos] = val;
  m_write_pos = next(m_write_pos);
  ++m_size;
}

template <class T>
void
circular_buffer<T>::overwrite(const value_type& val) {
  if (m_size != m_data.size()) {
    write(val);
    return;
  }
  
  m_data[m_write_pos] = val;
  m_read_pos = next(m_read_pos);
}

template <class T>
void
circular_buffer<T>::clear() {
  m_size = m_write_pos = m_read_pos = 0;
}

template <class T>
typename circular_buffer<T>::size_type
circular_buffer<T>::next(size_type x) const {
  return (x + 1) % m_data.size();
}
}

#endif // CIRCULAR_BUFFER_H

Tags:

construct:add
construct:assignment
construct:class
construct:comment
construct:constructor
construct:const
construct:field
construct:if
construct:index-operator
construct:initializer-list
construct:int
construct:integral-number
construct:invocation
construct:method
construct:method-overloading
construct:namespace
construct:number
construct:parameter
construct:return
construct:template
construct:throw
construct:using-declaration
construct:variable
construct:vector
construct:visibility-modifiers
paradigm:imperative
paradigm:object-oriented
technique:exceptions

@ErikSchierboom
Copy link
Member Author

ErikSchierboom commented Oct 31, 2023

Exercise: collatz-conjecture

Code

#pragma once
#include <cstdint>
#include <stdexcept>

namespace collatz_conjecture
{
    std::uint64_t steps(std::int64_t n)
    {
        if (n < 1)
        {
            throw std::domain_error("argument must be positive");
        }
        std::uint64_t steps = 0;
        while (n != 1)
        {
            if (n % 2 == 0)
            {
                n /= 2;
            }
            else
            {
                n = 3 * n + 1;
            }
            ++steps;
        }
        return steps;
    }
}

Tags:

construct:add
construct:assignment
construct:divide
construct:header
construct:if
construct:include
construct:integral-number
construct:invocation
construct:loop
construct:method
construct:multiply
construct:namespace
construct:number
construct:parameter
construct:pragma
construct:return
construct:string
construct:throw
construct:uint64_t
construct:variable
paradigm:imperative
technique:exceptions
technique:looping

@ErikSchierboom
Copy link
Member Author

ErikSchierboom commented Oct 31, 2023

Exercise: two-fer

Code

#include "two_fer.h"

namespace two_fer
{
    const std::string two_fer()
    {
        return "One for you, one for me.";
    }
    const std::string two_fer(std::string name)
    {
        return "One for " + name + ", one for me.";
    }

} // namespace two_fer

Tags:

construct:add
construct:comment
construct:const
construct:include
construct:method
construct:namespace
construct:overloading
construct:parameter
construct:return
construct:string

@ErikSchierboom
Copy link
Member Author

ErikSchierboom commented Oct 31, 2023

Exercise: two-fer

Code

#if !defined(TWO_FER_H)
#define TWO_FER_H
#include <string>

using namespace std;

namespace two_fer
{
    string two_fer(string one_fer = "you") {
        return "One for " + one_fer +", one for me.";
    }
} // namespace two_fer

#endif //TWO_FER_H

Tags:

construct:add
construct:comment
construct:define
construct:header
construct:include
construct:namespace
construct:optional-parameter
construct:return
construct:string
construct:using-directive

@vaeng
Copy link
Contributor

vaeng commented Nov 1, 2023

I am willing to help.

@vaeng
Copy link
Contributor

vaeng commented Nov 2, 2023

I'm done @ErikSchierboom

@vaeng
Copy link
Contributor

vaeng commented Nov 2, 2023

I "only" checked if the tags matched the exercise. I did not check if the tags are conform to https://exercism.org/docs/building/tooling/analyzers/tags. Should I do that as well?

@ErikSchierboom
Copy link
Member Author

Thanks! We'll do the cross-checking, don't worry!

@ErikSchierboom
Copy link
Member Author

This is an automated comment

Hello 👋 Next week we're going to start using the tagging work people are doing on these. If you've already completed the work, thank you! If you've not, but intend to this week, that's great! If you're not going to get round to doing it, and you've not yet posted a comment letting us know, could you please do so, so that we can find other people to do it. Thanks!

@ErikSchierboom
Copy link
Member Author

Thanks for the help! We've updated the tags.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants