text:
<file_sep>using System;
using System.Collections.Generic;
using TabloidCLI.Models;

namespace TabloidCLI.UserInterfaceManagers
{
    public class TagManager : IUserInterfaceManager
    {
        private readonly IUserInterfaceManager _parentUI;
        private TagRepository _tagRepository;

        public TagManager(IUserInterfaceManager parentUI, string connectionString)
        {
            _parentUI = parentUI;
            _tagRepository = new TagRepository(connectionString);
        }

        public IUserInterfaceManager Execute()
        {
            Console.WriteLine("---------------------------");
            Console.WriteLine("Tag Menu");
            Console.WriteLine("---------------------------");
            Console.WriteLine(" 1) List Tags");
            Console.WriteLine(" 2) Add Tag");
            Console.WriteLine(" 3) Edit Tag");
            Console.WriteLine(" 4) Remove Tag");
            Console.WriteLine(" 0) Go Back");

            Console.Write("> ");
            string choice = Console.ReadLine();
            switch (choice)
            {
                case "1":
                    List();
                    return this;
                case "2":
                    Add();
                    return this;

                case "3":
                    Edit();
                    return this;
                case "4":
                    Remove();
                    return this;
                case "0":
                    Console.Clear(); return _parentUI;
                default:
                    Console.WriteLine("Invalid Selection");
                    return this;
            }
        }

        private void List()
        {
            List<Tag> tags = _tagRepository.GetAll();
            foreach ( Tag tag in tags)
            {
                Console.WriteLine($"Tag title: {tag.Name}");
            }
        }

        private void Add()
        {
            Console.WriteLine("New Tag");
            Tag tag = new Tag();

            Console.Write("Tag Name: ");
            tag.Name = Console.ReadLine();

            _tagRepository.Insert(tag);
        }

        private void Edit()
        {
            Console.WriteLine("Which tag would you like to edit? ");
            List<Tag> tags = _tagRepository.GetAll();
            for (int i = 0; i < tags.Count; i++)
            {
                Tag tag= tags[i];
                Console.WriteLine($"{i + 1}) {tag.Name} ");
            }
            Console.Write("> ");

            bool indexCanParse = Int32.TryParse(Console.ReadLine(), out int indexToEdit);
            while(!indexCanParse)
            {
                Console.WriteLine("That was an invalid selection. Try again.");
                Console.Write("> ");

                indexCanParse = Int32.TryParse(Console.ReadLine(), out  indexToEdit);

            }

            Tag tagToEdit = tags[indexToEdit - 1];

            Console.WriteLine("New tag name:");
            string newName = Console.ReadLine();
            if (newName == "" || newName == null)
            {

            }
            else
            {
                tagToEdit.Name = newName;
            };

            _tagRepository.Update(tagToEdit);
        }

        private Tag Choose(string prompt = null)
        {
            if (prompt == null)
            {
                prompt = "Please choose a tag:";
            }

            Console.WriteLine(prompt);

            List<Tag> tags = _tagRepository.GetAll();

            for (int i = 0; i < tags.Count; i++)
            {
                Tag tag = tags[i];
                Console.WriteLine($" {i + 1}) {tag.Name}");
            }
            Console.Write("> ");

            string input = Console.ReadLine();
            try
            {
                int choice = int.Parse(input);
                return tags[choice - 1];
            }
            catch (Exception ex)
            {
                Console.WriteLine("Invalid Selection");
                return null;
            }
        }

        private void Remove()
        {
            Tag tagToDelete = Choose("Which tag would you like to remove?");
            if (tagToDelete != null)
            {
                try
                {
                    // Here we try to delete a tag using the Delete method found in TagRepository.cs
                    // If there are posts in the database that reference the ID of the tag we are trying to delete,
                    // a `Microsoft.Data.SqlClient.SqlException` error is thrown
                    _tagRepository.Delete(tagToDelete.Id);
                }
                catch (Microsoft.Data.SqlClient.SqlException)
                {
                    // If the error would be thrown, the try/catch statement stops the code above from executing
                    // and instead executes this code
                    Console.WriteLine("Cannot delete the selected tag while posts exist");
                }

            }
        }
    }
}
<file_sep>using System;
using System.Runtime.Serialization;

namespace TabloidCLI.UserInterfaceManagers
{
    [Serializable]
    internal class SearchResults : Exception
    {
        private MainMenuManager mainMenuManager;
        private string cONNECTION_STRING;

        public SearchResults()
        {
        }

        public SearchResults(string message) : base(message)
        {
        }

        public SearchResults(MainMenuManager mainMenuManager, string cONNECTION_STRING)
        {
            this.mainMenuManager = mainMenuManager;
            this.cONNECTION_STRING = cONNECTION_STRING;
        }

        public SearchResults(string message, Exception innerException) : base(message, innerException)
        {
        }

        protected SearchResults(SerializationInfo info, StreamingContext context) : base(info, context)
        {
        }
    }
}<file_sep>using System;
using System.Collections.Generic;
using System.Text;

namespace TabloidCLI.UserInterfaceManagers
{
    class BackgroundColorManager : IUserInterfaceManager
    {
        public IUserInterfaceManager Execute()
        {
            // Need to show list of available colors here

            //  Get an array with the values of ConsoleColor enumeration members
            ConsoleColor[] colors = (ConsoleColor[])ConsoleColor.GetValues(typeof(ConsoleColor));

            // Save the current colors in the background and foreground
            ConsoleColor currentBackground = Console.BackgroundColor;
            ConsoleColor currentForeground = Console.ForegroundColor;

            // Display all background colors except the one that matches the foreground
            // (We want to make sure the text on the screen is visible!)
            Console.WriteLine($"All the background colors except {currentForeground}, the foreground color");

            for (int i = 0; i < colors.Length; i++)
            {
                if (colors[i] == currentForeground) continue;

                Console.WriteLine($"{i + 1} {colors[i]}");
            }

            Console.Write("> ");
            string input = Console.ReadLine();
            try
            {
                int choice = (int.Parse(input) - 1);
                ConsoleColor colorChoice = colors[choice];
                Console.BackgroundColor = colorChoice;
                // Do we return something, or just set it here?
                Console.Clear();
                return new MainMenuManager();
            }
            catch (Exception ex)
            {
                Console.WriteLine("Invalid Selection");
                // Do we return 'this' to re-run the color choice process?
                return this;
            }

        }
    }
}
<file_sep>using System;
using System.Collections.Generic;
using TabloidCLI.Models;
using TabloidCLI.Repositories;

namespace TabloidCLI.UserInterfaceManagers
{
    public class PostManager : IUserInterfaceManager
    {
        private readonly IUserInterfaceManager _parentUI;
        private PostRepository _postRepository;
        private AuthorRepository _authorRepository;
        private TagRepository _tagRepository;
        private BlogRepository _blogRepository;
        private int _postId;

        public PostManager(IUserInterfaceManager parentUI, string connectionString)
        {
            _parentUI = parentUI;
            _postRepository = new PostRepository(connectionString);
            _tagRepository = new TagRepository(connectionString);
            _authorRepository = new AuthorRepository(connectionString);
            _blogRepository = new BlogRepository(connectionString);

        }

        public IUserInterfaceManager Execute()
        {
            Console.WriteLine("---------------------------");
            Console.WriteLine("Post Menu");
            Console.WriteLine("---------------------------");
            Console.WriteLine(" 1) List Posts");
            Console.WriteLine(" 2) Post Details");
            Console.WriteLine(" 3) Add Post");
            Console.WriteLine(" 4) Edit Post");
            Console.WriteLine(" 5) Remove Post");
            Console.WriteLine(" 0) Go Back");

            Console.Write("> ");
            string choice = Console.ReadLine();

            switch (choice)
            {
                case "1":
                    List();
                    return this;
                case "2":
                    Choose();

                    return this;

                case "3":
                    Add();
                    return this;
                case "4":
                    Edit();
                    return this;
                case "5":
                    Remove();
                    return this;
                case "0":
                    Console.Clear(); return _parentUI;
                default:
                    Console.WriteLine("Invalid Selection");
                    return this;
            }
        }

        private void List()
        {
            List<Post> posts = _postRepository.GetAll();
            foreach (Post post in posts)
            {
                Console.WriteLine($"Post title: {post.Title}");
                Console.WriteLine($"Post URL: {post.Url}");
            }
        }

        private void Choose(string prompt = null)
        {
            if (prompt == null)
            {
                prompt = "Please choose a Post:";
            }

            Console.WriteLine(prompt);

            List<Post> posts = _postRepository.GetAll();

            for (int i = 0; i < posts.Count; i++)
            {
                Post post = posts[i];
                Console.WriteLine($"{ i + 1}) {post.Title}");
            }
            Console.Write("> ");

            string input = Console.ReadLine();
            try
            {
                int choice = int.Parse(input);
                Post chosenPost = posts[choice - 1];
                Console.WriteLine(chosenPost.Id);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Invalid Selection");

            }

        }

        private void Add()
        {
            Console.WriteLine("New Post");
            Post post = new Post();

            Console.WriteLine("Title of post: ");
            post.Title = Console.ReadLine();

            Console.WriteLine("URL of post: ");
            post.Url = Console.ReadLine();

            Console.WriteLine("Publication Date (MM/DD/YYYY)");
            bool canDate = DateTime.TryParse(Console.ReadLine(), out DateTime result);
            while ( !canDate )
            {
                Console.WriteLine("Please enter a valid Publication Date (MM/DD/YYYY)");
                canDate = DateTime.TryParse(Console.ReadLine(), out result);
            };
            post.PublishDateTime = result;

            Console.WriteLine("Who is the author of post: ");
            List<Author> authors = _authorRepository.GetAll();
            for (int i = 0; i < authors.Count; i++)
            {
                Author author = authors[i];
                Console.WriteLine($" {i + 1}) {author.FullName}");
            }
            Console.Write("> ");

            string input = Console.ReadLine();
            try
            {
                int choice = int.Parse(input);
                post.Author = authors[choice - 1];
            }
            catch (Exception ex)
            {
                Console.WriteLine("Invalid Selection");

            }

            Console.WriteLine("Choose an associated blog post: ");
            List<Blog> blogs = _blogRepository.GetAll();
            for (int i = 0; i < blogs.Count; i++)
            {
                Blog blog = blogs[i];
                Console.WriteLine($" {i + 1}) {blog.Title}");
            }
            Console.Write("> ");

            string blogInput = Console.ReadLine();
            try
            {
                int choice = int.Parse(blogInput);
                post.Blog = blogs[choice - 1];
            }
            catch (Exception ex)
            {
                Console.WriteLine("Invalid Selection");

            }

            _postRepository.Insert(post);

        }

        private void Edit()
        {
            Console.WriteLine("Which post would you like to edit?");
            List<Post> posts = _postRepository.GetAll();

            for (int i = 0; i < posts.Count; i++)
            {
                Post post = posts[i];
                Console.WriteLine($" {i + 1}) {post.Title}");
            }
            Console.Write("> ");

            int postToEditIndex = Int32.Parse(Console.ReadLine() );

            Post postToEdit = posts[postToEditIndex - 1];

            // Here menu to get updates

            Console.WriteLine("Title of post: ");
            string newTitle = Console.ReadLine();
            if (newTitle == "" || newTitle == null)
            {

            } else
            {
                postToEdit.Title = newTitle;
            };

            Console.WriteLine("URL of post: ");
            string newURL = Console.ReadLine();
            if (newURL == null || newURL == "")
            {

            }
            else
            {
                postToEdit.Url = newURL;
            };

            Console.WriteLine("Publication Date (MM/DD/YYYY)");
            bool canDate = DateTime.TryParse(Console.ReadLine(), out DateTime result);
            if (canDate)
            {
                postToEdit.PublishDateTime = result;
            };

            Console.WriteLine("Who is the author of post: ");
            List<Author> authors = _authorRepository.GetAll();
            for (int i = 0; i < authors.Count; i++)
            {
                Author author = authors[i];
                Console.WriteLine($" {i + 1}) {author.FullName}");
            }
            Console.Write("> ");

            string input = Console.ReadLine();
            try
            {
                int choice = int.Parse(input);
                postToEdit.Author = authors[choice - 1];
            }
            catch (Exception ex)
            {
                Console.WriteLine("Invalid Selection");

            }

            Console.WriteLine("Choose an associated blog post: ");
            List<Blog> blogs = _blogRepository.GetAll();
            for (int i = 0; i < blogs.Count; i++)
            {
                Blog blog = blogs[i];
                Console.WriteLine($" {i + 1}) {blog.Title}");
            }
            Console.Write("> ");

            string blogInput = Console.ReadLine();
            try
            {
                int choice = int.Parse(blogInput);
                postToEdit.Blog = blogs[choice - 1];
            }
            catch (Exception ex)
            {
                Console.WriteLine("Invalid Selection");

            }

            _postRepository.Update(postToEdit);
        }

        private void Remove()
        {
            Console.WriteLine("Which post would you like to delete?");
            List<Post> posts = _postRepository.GetAll();

            for (int i = 0; i < posts.Count; i++)
            {
                Post post = posts[i];
                Console.WriteLine($" {i + 1}) {post.Title}");
            }
            Console.Write("> ");

            string choice = Console.ReadLine();
            try
            {
                int postToDelete = Int32.Parse(choice);
                _postRepository.Delete(posts[postToDelete - 1].Id);
            }
            catch
            {
                Console.WriteLine("Invalid Selection");
            }

        }
    }

}
<file_sep>using System;
using System.Collections.Generic;
using System.Text;
using TabloidCLI.Models;
using TabloidCLI.Repositories;

namespace TabloidCLI.UserInterfaceManagers
{
    internal class TagDetailManager: IUserInterfaceManager
    {
        private IUserInterfaceManager _parentUI;
        private BlogRepository _blogRepository;
        private PostRepository _postRepository;
        private TagRepository _tagRepository;
        private int _tagId;

        public TagDetailManager(IUserInterfaceManager parentUI, string connectionString, int authorId)
        {
            _parentUI = parentUI;
            _blogRepository = new BlogRepository(connectionString);
            _postRepository = new PostRepository(connectionString);
            _tagRepository = new TagRepository(connectionString);
            _tagId = authorId;
        }

        public IUserInterfaceManager Execute()
        {
            Tag tag = _tagRepository.Get(_tagId);
            Console.WriteLine($"{tag.Name} Details");
            Console.WriteLine(" 1) View");
            Console.WriteLine(" 0) Go Back");

            Console.Write("> ");
            string choice = Console.ReadLine();
            switch (choice)
            {
                case "1":
                    View();
                    return this;
                case "0":
                    return _parentUI;
                default:
                    Console.WriteLine("Invalid Selection");
                    return this;
            }
        }

        private void View()
        {
            Tag tag = _tagRepository.Get(_tagId);
            Console.WriteLine($"Name: {tag.Name}");
            Console.WriteLine();
        }

    }
}

branch_name: refs/heads/main

repo_language: C#


text:
<repo_name>Tolashuang/Use-YOLO-to-train-your-own-database<file_sep>/README.md
# Use-YOLO-to-train-your-own-database
The step of use YOLO to train your own database

【利用YOLO训练自己的数据】

1.收集数据集以及制作标签：xml或者json格式

2.应用脚本文件对xml或json文件进行处理，提取出：
    类别索引object box中心坐标x/整个图片的w box中心坐标y/整个图片的h box的w/整个图片的w box的h/整个图片的h，
    并且，生成txt的形式要和训练图片的格式(文件夹存储结构)、名称完全一致

	eg.

    1 0.716797 0.395833 0.216406 0.147222

    0 0.687109 0.379167 0.255469 0.158333

    1 0.420312 0.395833 0.140625 0.166667

3.生成train图片的路径：

	①直接用训练图片生成：
	find /home/chensc/JPEGImage/...(绝对路径，图片根文件夹)/ | grep(查找的意思) jpg（要查找的包含关键字） > train.txt
	然后将生成的train.txt放到XXX-project中

    ②用json转的labels中txt文件转换(因为这些labels中txt文件和图片名称对应)：

      (1)find /home/chensc/labels/...(绝对路径，特征labels的根文件夹)/ | grep(查找的意思) txt（要查找的包含关键字） > train.txt
      (2)sed -i 's/labels（被替换的）/JPEGImage（替换后的）/g' train.txt（文件路径）
      (3)sed -i 's/txt/jpg/g' train.txt

4.在darknet中新建文件夹XXX-project，包括:

    ①XXX.cfg（网络模型）：
    [convolutional]
    filters=5*(classes+4+1)

    [region]
    classes=n

    ②obj.names：使用对象名称，每个都一行

    ③obj.data：（绝对/相对路径都可以）
          classes= n
          train  = XXX-project/train.txt
          valid  = XXX-project/test.txt（可以和train一样）
          names = XXX-project/obj.names
          backup = XXX-project/backup/ （存训练后模型的位置）

    ④为卷积图层下载预先训练的权重（76 MB）：http : //pjreddie.com/media/files/darknet19_448.conv.23，放到XXX-project

5.训练：cd 到darknet中

    ./darknet detector train XXX-project/obj.data XXX-project/XXX.cfg darknet19_448.conv.23(预训练的网络模型) 10(修改网络后，只取前10层) -clear(此处是为了将预训练迭代过的次数清0，使得cfg中的max batch可以直接写要迭代的次数) > log.txt(打印log日志)

6.可视化：

    ①将log.txt拷贝到包含.sh和.py的文件夹下
    ②bash general_cifar_log.sh log.txt，这是会有hand_log_data.txt生成
    ③python analzy_log.py

注意：

    1.yolo在训练的时候，obj、recall、IOU、calss以及loss就已经能表示训练的好坏了，val、test用不上，都写成和train一样的就行

    2.训练时候xml、json标注文件就没有用了，只有txt文件

    3.txt中类别只能用索引0123...表示，对应obj.names中的类别

    4.训练集文件夹名字只能叫：JPEGImages；标签文件夹名字只能叫：labels
    （解释）：必须把训练集JPEGImages和标签labels放在一起，darknet之所以不用标签的路径，是因为读取图片路径时候是自动把JPEGImages换成labels、把png换成txt

    5.cfg文件解析：(windows下写cfg会有回车键，ubuntu解析会出错，vi -b XXX.cfg ，delete删除^M)

        ①batch是并行运算的图片数量：64,128，指的是64张图片计算一个loss；subdivisions是将batch分成几份来跑，防止显存爆炸

        ②数据增强：angle=0、saturation = 1.5、exposure = 1.5、hue=.1

        ③初始学习率：learning_rate=0.0001、最大迭代次数：
                    max_batches = 100000、policy=steps、改变学习率的迭代次数：steps=100, 50000, 80000、每次改变学习率的倍数：scales=10,.1,.1
                    过程解释：此过程中初始到100次迭代学习率是0.0001，100-50000迭代是0.001,50000-80000迭代是0.0001,80000-100000迭代是0.00001
                    正常过程：应该是先给一个大的学习率，然后随着迭代次数的增加，应该减小学习率
                    初始膨胀学习率：为了防止大学习率跑出nan，最开始给一个小的学习率先跑到一个局部最优解，然后膨胀学习率跑出局部最优解，
                    在之后慢慢减小学习率

        ④yolo v2网络若输入是416*416，那最后的特征图输出应该必须是13*13(如果因为预训练中修改了输入图片大小，导致输出不是13*13了，需要修改网络)

        ⑤倒数第二层以及最后一层是：
        [convolutional]
        batch_normalize=1
        filters=512
        size=3
        stride=1
        pad=1
        activation=leaky
        #预训练锁死权重，等到loss不在下降时候在不锁权重(这样是因为最开始反向传播会打乱本来预训练好的模型的权重，不锁死的话特征白提取了)
        stopbackward=1

        [convolutional]
        size=3
        stride=1
        pad=1
        filters=35 #(classes+4+1)*anchor数5
        activation=linear #线性分类器

        ⑥后面：
        [region]
        anchors = 0.71, 5.15,  0.39, 1.21,  1.83, 3.46,  0.94, 1.83,  3.15, 6.56 #anchors尺寸大小，k-means算法得出
        bias_match=1
        classes=2 #类别数
        coords=4
        num=5 #每个单元格预测的anchors数
        softmax=1
        jitter=.2
        rescore=1

        #损失函数的权值
        object_scale=5 #包含目标的定位误差
        noobject_scale=1 #不包含目标的误差
        class_scale=1 #分类误差
        coord_scale=1 #包含目标的置信度误差

        #这些参数大多数网络都是这样的设置
        absolute=1
        thresh = .6
        random=1

        附上yolo参数解释帖子网址：http://blog.csdn.net/zhuiqiuk/article/details/70167963

        ⑦训练时候的log中，迭代次数后面的两个数字是loss，第一个是batch的loss，第二个是整个平均的loss

        ⑧接着之前的训练：
        ./darknet detector train XXX-project/obj.data XXX-project/XXX.cfg darknet.backup(backup中产生的一个weights文件)  >> log.txt(用>>可以接着原来的log.txt继续打印日志)

        此处不能加-clean，-clean是将以前预训练的模型中记录的迭代次数清0，使得学习率在cfg文件里根据迭代次数判断，进行减小。
        如果接着之前的训练加-clean的话，那么迭代次数会被清0，这样学习率就会从初始的开始，对应不上之前训练的学习率。
<file_sep>/XML_Parse.py
import os
from PIL import Image
from xml.etree import ElementTree

try:
    import xml.etree.cElementTree as ET
except ImportError:
    import xml.etree.ElementTree as ET

classes = ["aeroplane", "bicycle", "bird", "boat", "bottle", "bus", "car", "cat", "chair", "cow", "diningtable", "dog", "horse", "motorbike", "person", "pottedplant", "sheep", "sofa", "train", "tvmonitor"]

def find_x_y_w_h(size, box):
    print(size[0])
    dw = 1./size[0]
    dh = 1./size[1]
    x = (box[0] + box[1])/2.0
    y = (box[2] + box[3])/2.0
    w = box[1] - box[0]
    h = box[3] - box[2]
    x = x*dw
    w = w*dw
    y = y*dh
    h = h*dh
    return (x,y,w,h)

os.chdir(r"D:/yolo_train/data/Annotations/")
files = os.listdir(r"D:/yolo_train/data/Annotations/")

for fout in files:
    foutName = "D:/yolo_train/data/Annotations/" + fout
    print(foutName)
    fin = open("D:/yolo_train/data/train/" + fout[:-4] + ".txt", "w+")

    with open(foutName , 'r') as f:
        tree = ET.parse(f)
        root = tree.getroot()

    for size in root.findall('size'):
        width = size.find('width').text
        height = size.find('height').text
        size =(float(width),float(height))
        print(size)

    for object in root.findall('object'):
        name = object.find('name').text
        print(name)

        bndbox = object.find('bndbox')
        xmin = bndbox.find('xmin').text
        xmax = bndbox.find('xmax').text
        ymin = bndbox.find('ymin').text
        ymax = bndbox.find('ymax').text
        box = (float(xmin),float(xmax),float(ymin),float(ymax))
        four_out = find_x_y_w_h(size,box)

        print xmin, xmax, ymin, ymax
        for i in range(len(classes)):
            if name == classes[i]:
                print(classes[i])
                print(i)
                fin.writelines("%d %f %f %f %f" % (i,four_out[0],four_out[1], four_out[2], four_out[3]))
                fin.writelines("\n")
    fin.close()

branch_name: refs/heads/master

languages: Markdown Python

repo_language: Markdown


text:
<repo_name>codeleaked/blog<file_sep>/src/pages/algorithm/2019-08-06---All-Paths-from-Source-to-Target/index.md
---
title: All paths from source to target
date: "2019-08-06T14:32:37.121Z"
layout: post
draft: false
path: "/posts/all-paths-from-source-to-target/"
category: "Algorithm"
tags:
  - "Graph"
  - "Learning to write"
description: "Description here"
---

Given a directed, acyclic graph of `N` nodes. Find all possible paths from node `0` to node `N-1`, and return them in any order.

The graph is given as follows: the nodes are `0, 1, ..., graph.length - 1`. `graph[i]` is a list of all nodes j for which the edge (i, j) exists.

##Example:

```
Input: [[1,2], [3], [3], []]
Output: [[0,1,3],[0,2,3]]
Explanation: The graph looks like this:
0--->1
|    |
v    v
2--->3
There are two paths: 0 -> 1 -> 3 and 0 -> 2 -> 3.
```

##Note:

- The number of nodes in the graph will be in the range `[2, 15]`.
- You can print different paths in any order, but you should keep the order of nodes inside one path.

## Solution:
```java
List<List<Integer>> allPathsSourceTarget(int[][] graph) {
    int[][] matrix = buildMatrix(graph);

    List<List<Integer>> paths = new ArrayList<>();
    List<Integer> path = new ArrayList<>();
    path.add(0);

    visit(0, matrix, path, paths);

    return paths;
}

private int[][] buildMatrix(int[][] graph) {
    int n = graph.length;
    int[][] matrix = new int[n][n];

    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < graph[i].length; ++j) {
            matrix[i][graph[i][j]] = 1;
        }
    }

    return matrix;
}

private void visit(int curNode, int[][] matrix, List<Integer> path, List<List<Integer>> paths) {
    if (curNode == matrix.length - 1) {
        paths.add(path);
    }
    for (int node = 0; node < matrix.length; ++node) {
        if (matrix[curNode][node] == 1) {
            List<Integer> newPath = new ArrayList<>(path);
            newPath.add(node);
            visit(node, matrix, newPath, paths);
        }
    }
}
```
<file_sep>/src/pages/algorithm/2019-08-06---Solving-Sudoku-with-CPLEX/index.md
---
title: Solving Sudoku with CPLEX
date: "2019-08-06T14:32:37.121Z"
layout: post
draft: false
path: "/posts/solving-sudoku-with-cplex/"
category: "Optimization"
tags:
  - "CPLEX"
  - "Learning to write"
description: "Description here"
---

We first create an IloCplex object

repo_name: codeleaked/blog


text:
<file_sep>source "http://rubygems.org"

gem "rake"
gem "roda"
gem "oj"
gem "opal", "~> 0.7.2"
# gem "opal-browser"

# web server
gem "thin" # or puma are good for dev, unicorn is best in prod - passenger+nginx is good when you have a lot of sites/apps on the same server or
#gem "thin"
#gem "puma"
#gem "passenger"
#gem "unicorn"

group :development do
  gem 'guard-shell'
  gem 'guard-concat'
  gem 'guard-sass'
end
<file_sep>require 'bundler'
Bundler.require

Opal.append_path "./"

File.binwrite(
  "dist/bundle.js",
    Opal::Builder.build("bundle").to_s
)
<file_sep>require_relative "config/env_server"

require 'net/http'

class BCPen < Roda

  plugin :static, %w( /dist /vendor /css /img ), root: APP_PATH

  route do |r|
    # GET / request
    r.root do
      File.read "./index.html"
    end

    r.on "test" do
      r.is do
        r.post do
          tx_hash = request.params["tx"]
          uri = URI "https://api.blockcypher.com/v1/btc/main/txs/push"
          response = Net::HTTP.post_form uri, { tx: tx_hash }
          response.body
        end
      end
    end
  end
end

# curl https://api.chain.com/v2/bitcoin/transactions/send \
#   -u '<KEY>:API-KEY-SECRET' \
#   -d '{"signed_hex": "0100000001pk..."}' \
#   -X POST
<file_sep>module UIHelpers
  extend DebugHelpers
  include DebugHelpers

  def q(selector)
    `document.querySelector(selector)`
  end

  def write(elem, content)
    `#{elem}.innerHTML = '#{content}'`
  end
end
<file_sep>`console.log("loading app environment")`

`self.$require("browser");`
`self.$require("browser/http");`

`window.bitcore = require('bitcore')`

class HTTP
  def self.get(url, callback)
    `$.getJSON(url, function(data){
      console.log("GET", url)
      callback(data)
    })`
  end

  def self.post(url, params, callback)
    # console.log
    # url = "/test"
    `var success = function(data){
      console.log("POST", url)
      callback(data)
    }`

    `var data = {
      tx: params.tx
    }

    console.log(JSON.stringify(data))
    `

    `ajax = {
      contentType: 'application/json',
      data: JSON.stringify(data),
      dataType: 'json',
      processData: false,
      type: 'POST',
      success: success,
      url: url
    }`

    `$.ajax(ajax)`

    # `$.ajax(url, ajax, function(data){
    #   console.log("POST", url)
    #   callback(data)
    # })`
  end
end

module RModel
  def attributes
    attrs = instance_variables.map{ |a| a.to_s[1..-1] }
    (attrs - ["constructor", "toString"]).map(&:to_sym)
  end
end

module DebugHelpers
  def log(*mex)
    if mex.size == 1
      `console.log(mex[0])`
    else
      `console.log(mex[0], mex[1])`
    end
  end
end

module UIHelpers
  extend DebugHelpers
  include DebugHelpers

  def q(selector)
    `document.querySelector(selector)`
  end

  def write(elem, content)
    `#{elem}.innerHTML = '#{content}'`
  end
end

class Blockchain

  def self.utxo(address, callback)
    `console.log(address)`
    utxo_url = "https://blockchain.info/unspent?active=#{address}&format=json&cors=true"

    HTTP.get utxo_url, self.utxo_callback(callback)
  end

  def self.utxo_callback(callback)
    -> (utxo) do
      `callback(utxo.unspent_outputs)`
    end
  end

  def self.pushtx(tx_hash, callback)
    pushtx_url = "https://api.blockcypher.com/v1/btc/main/txs/push"
    post_params = { tx: tx_hash }.to_n
    # pushtx_url = "https://insight.bitpay.com/api/tx/send"
    # post_params = { rawtx: tx_hash }.to_n
    HTTP.post pushtx_url, post_params, self.pushtx_callback(callback)
  end

  def self.pushtx_callback(callback)
    -> (tx_info) do
      `console.log("TX INFO", tx_info)`
      `callback(tx_info.tx.hash)`
    end
  end

end

class PrivateKey
  def initialize(key)
    @key = `new bitcore.PrivateKey(key)`
  end

  def address
    key = @key
    `key.toAddress()`
  end

  def address_str
    address = self.address
    `address.toString()`
  end
end

require 'native'
require 'json'

class BitCore
  include DebugHelpers

  # TX_FEE = 8000   # old fee (~10k sat)
  # TX_FEE = 29_000 # min fee (2017)
  TX_FEE = 34_000 # good fee
  # TX_FEE = 40_000 # big fee (quick confirm)

  def initialize(pvt_key_string)
    @pvt_key_string = pvt_key_string
    @pvt_key = PrivateKey.new pvt_key_string
    log 'pvt key', @pvt_key
    @address = @pvt_key.address_str
    log 'address', @address
  end

  def sign_and_broadcast
    -> (message, utxos, callback) do
      log "sign and broadcast"
      tx_amount = 1000

      utxos = hashes_convert utxos

      log "utxo_size", utxos.size
      utxos_out = []
      total_amount_sathoshis = 0

      # TODO: save utxo used in local storage if the transaction succeeded in cache for 2 minutes
      # lock utxo and don't reuse it

      utxos.each do |utxo|
        amount_satoshis = utxo["value"]
        total_amount_sathoshis += amount_satoshis
        amount_btc = `new bitcore.Unit.fromSatoshis(amount_satoshis).BTC`
        log amount_btc
        tx_id = utxo["tx_hash_big_endian"]

        if store.utxos && JSON.parse(store.utxos).include?(tx_id)
          log "skipping transaction: #{tx_id}"
          next
        end

        utxos_out.push({
          address:      @address,
          txId:         tx_id,
          scriptPubKey: utxo["script"],
          amount:       amount_btc,
          vout:         utxo["tx_output_n"],
        })
        break if amount_satoshis > TX_FEE+tx_amount
      end
      log "utxos_out:",  utxos_out.size

      unless utxos.empty?
        fee = TX_FEE # from 5000 it should be a good fee
        utxos_out = utxos_out.to_n
        address = @address
        amount  = tx_amount
        pvt_key = @pvt_key_string
        log "utxos_out: ", utxos_out

        transaction = `new bitcore.Transaction()
          .from(utxos_out)
          .to(address, amount)
          .change(address)
          .fee(fee)
          .addData(message)
          .sign(pvt_key)`

        # log transaction
        tx_hash = `transaction.serialize()`
        log tx_hash

        Blockchain.pushtx tx_hash, self.pushtx_callback(utxos_out, callback)

        # TODO:
        #
        # try {
        #
        #   txHash = transaction.serialize();
        # } catch(error) {
        #
        #   reject({
        #     'message': 'Error serializing the transaction: ' + error.message
        #   });
        # }
      else
        log "ERROR: Not enough UTXOs"
      end

      log "END"

    end
  end

  def pushtx_callback(utxos_out, callback)
    -> (tx_id) do
      log "save in localStorage", tx_id
      log "utxos_out: ", utxos_out

      utxos = store.utxos ? JSON.parse(store.utxos) : []
      utxos = utxos + utxos_out.map{ |utxo| `utxo.txId` }
      store.utxos = utxos.to_json

      log "received tx_id:", tx_id
      log "TX pushed!!!"

      callback.(tx_id)
    end
  end

  def received_utxo(message, callback)
    -> (utxo) do
      log "received UTXO:", utxo
      self.sign_and_broadcast.(message, utxo, callback)
    end
  end

  def op_return(message, callback)
    Blockchain.utxo @address, self.received_utxo(message, callback)
  end

  def store
    Native(`localStorage`)
  end

  # TODO: use require 'json'; JSON.parse; zepto get
  def hashes_convert(array)
    Array.new(array).map do |elem|
      Hash.new elem
    end
  end

end

class Pen
  extend DebugHelpers

  def self.pvt_key
    "<KEY>" # <KEY>
  end

  def self.write(message, callback)
    log "stylus - preparing to write: #{message}"
    bitcore = BitCore.new(pvt_key)
    bitcore.op_return message, self.callback_write(callback)
  end

  def self.callback_write(callback)
    -> (tx_id) do
      log 'stylus - wrote message! tx_id:', tx_id
      log "https://live.blockcypher.com/btc/tx/#{tx_id}"
      log "https://blockchain.info/tx/#{tx_id}"
      callback.(tx_id)
    end
  end

end

class Wallet
  include RModel

  attr_accessor :address, :balance

  def initialize(address:, balance:)
    @address = address
    @balance = balance
  end

  TEST = ->{ Wallet.new address: "1asd", balance: 10_000 }
end

# test
# w = Wallet::TEST.(); puts w.attributes

class Hasher

  def self.hash_file

  end

  def self.hash(file)

   `

    reader = new FileReader();
    reader.onload = function(data) {
      window.crypto.subtle.digest(
        {
            name: "SHA-256",
        },
        data
      )
      .then(function(hash){
        console.log(new Uint8Array(hash))
      })
      .catch(function(err){
          console.error(err)
      })
    }
    reader.readAsArrayBuffer(file)

    `
  end

end

class MessageForm
  include React::Component
  extend DebugHelpers
  extend UIHelpers
  include UIHelpers

  define_state(:chars)  { 0 }
  define_state(:submit_disabled)  { false }
  define_state(:tx_id)  { nil }
  define_state(:loading) { false }

  MAX_CHARS = 75

  def write
    log "writing message: #{self.message}"
    self.loading = true
    Pen.write self.message, self.callback_write
  end

  def message
    # mex = q "input[name=message]"
    `document.querySelector("input[name=message]").value`
  end

  def update_counter
    self.chars = String.new(message).size
    self.submit_disabled = true if self.chars > MAX_CHARS
  end

  def render
    div className: "message_input" do
      div className: "row align-right" do
        span do
          self.chars
        end
        span do
          " / #{MAX_CHARS} chars"
        end
      end
      spacer
      div className: "row" do
        div className: "five columns" do
          input(name: "message", placeholder: "your important message...", type: "text")
            .on(:change){ update_counter }
        end
        div className: "one columns" do
          button(disabled: self.submit_disabled) do
            "Write"
          end.on(:click){ write }
        end
      end
      div className: "spinner" do
        span { "loading..." }
      end if self.loading
      if self.tx_id
        div className: "row" do
          div className: "spacer30"
          div { "the message has been written: #{self.tx_id}" }
          div do
            span do
              a href: "https://live.blockcypher.com/btc/tx/#{self.tx_id}" do
                "blockcypher.com"
              end
            end
            span { " - " }
            span do
              a href: "https://blockchain.info/tx/#{self.tx_id}" do
                "blockchain.info"
              end
            end
            span { " - " }
            span do
              a href: "https://chain.so/tx/BTC/#{self.tx_id}" do
                "chain.so"
              end
            end
            span { " - " }
            span do
              a href: "http://eternitywall.it/m/#{self.tx_id}" do
                "eternitywall.it"
              end
            end
          end
        end
      end
    end
  end

  def callback_write
    -> (tx_id) do
      self.tx_id = tx_id
      self.loading = false
    end
  end

  def spacer
    div className: "spacer10"
  end
end

class FileForm
  include React::Component
  extend DebugHelpers

  define_state(:submit_disabled)  { false }

  def hash_file
   Hasher.hash `document.querySelector("input[name=file]").files[0]`
   `console.log("hash file called!!!")`
  end

  def render
    div className: "message_input" do
      div className: "row" do
        div className: "five columns" do
          input name: "file", type: "file"
        end
        div className: "one columns" do
          button(disabled: self.submit_disabled) do
            "Write hash"
          end.on(:click){ hash_file }
        end
      end
    end
  end
end

class Success
  include React::Component

  define_state(:tx_id) { "" }

  def render
    div className: "success" do
      p "Message written:"
      p self.tx_id
    end
  end
end

class BCPen
  include React::Component

  def render
    div className: "bc_stylus" do
      present MessageForm
      # present FileForm # alpha
    end
  end
end

extend UIHelpers

log "loading app.rb"

content = q ".content"

React.render(
  React.create_element(BCPen),
  `content`
)
<file_sep>extend UIHelpers

log "loading app.rb"

content = q ".content"

React.render(
  React.create_element(BCPen),
  `content`
)
<file_sep>`console.log("loading app environment")`

`self.$require("browser");`
`self.$require("browser/http");`

`window.bitcore = require('bitcore')`
<file_sep>class PrivateKey
  def initialize(key)
    @key = `new bitcore.PrivateKey(key)`
  end

  def address
    key = @key
    `key.toAddress()`
  end

  def address_str
    address = self.address
    `address.toString()`
  end
end
<file_sep>class Pen
  extend DebugHelpers

  def self.pvt_key
    "<KEY>" # <KEY>
  end

  def self.write(message, callback)
    log "stylus - preparing to write: #{message}"
    bitcore = BitCore.new(pvt_key)
    bitcore.op_return message, self.callback_write(callback)
  end

  def self.callback_write(callback)
    -> (tx_id) do
      log 'stylus - wrote message! tx_id:', tx_id
      log "https://live.blockcypher.com/btc/tx/#{tx_id}"
      log "https://blockchain.info/tx/#{tx_id}"
      callback.(tx_id)
    end
  end

end
<file_sep>require 'native'
require 'json'

class BitCore
  include DebugHelpers

  # TX_FEE = 8000   # old fee (~10k sat)
  # TX_FEE = 29_000 # min fee (2017)
  TX_FEE = 34_000 # good fee
  # TX_FEE = 40_000 # big fee (quick confirm)

  def initialize(pvt_key_string)
    @pvt_key_string = pvt_key_string
    @pvt_key = PrivateKey.new pvt_key_string
    log 'pvt key', @pvt_key
    @address = @pvt_key.address_str
    log 'address', @address
  end

  def sign_and_broadcast
    -> (message, utxos, callback) do
      log "sign and broadcast"
      tx_amount = 1000

      utxos = hashes_convert utxos

      log "utxo_size", utxos.size
      utxos_out = []
      total_amount_sathoshis = 0

      # TODO: save utxo used in local storage if the transaction succeeded in cache for 2 minutes
      # lock utxo and don't reuse it

      utxos.each do |utxo|
        amount_satoshis = utxo["value"]
        total_amount_sathoshis += amount_satoshis
        amount_btc = `new bitcore.Unit.fromSatoshis(amount_satoshis).BTC`
        log amount_btc
        tx_id = utxo["tx_hash_big_endian"]

        if store.utxos && JSON.parse(store.utxos).include?(tx_id)
          log "skipping transaction: #{tx_id}"
          next
        end

        utxos_out.push({
          address:      @address,
          txId:         tx_id,
          scriptPubKey: utxo["script"],
          amount:       amount_btc,
          vout:         utxo["tx_output_n"],
        })
        break if amount_satoshis > TX_FEE+tx_amount
      end
      log "utxos_out:",  utxos_out.size

      unless utxos.empty?
        fee = TX_FEE # from 5000 it should be a good fee
        utxos_out = utxos_out.to_n
        address = @address
        amount  = tx_amount
        pvt_key = @pvt_key_string
        log "utxos_out: ", utxos_out

        transaction = `new bitcore.Transaction()
          .from(utxos_out)
          .to(address, amount)
          .change(address)
          .fee(fee)
          .addData(message)
          .sign(pvt_key)`

        # log transaction
        tx_hash = `transaction.serialize()`
        log tx_hash

        Blockchain.pushtx tx_hash, self.pushtx_callback(utxos_out, callback)

        # TODO:
        #
        # try {
        #
        #   txHash = transaction.serialize();
        # } catch(error) {
        #
        #   reject({
        #     'message': 'Error serializing the transaction: ' + error.message
        #   });
        # }
      else
        log "ERROR: Not enough UTXOs"
      end

      log "END"

    end
  end

  def pushtx_callback(utxos_out, callback)
    -> (tx_id) do
      log "save in localStorage", tx_id
      log "utxos_out: ", utxos_out

      utxos = store.utxos ? JSON.parse(store.utxos) : []
      utxos = utxos + utxos_out.map{ |utxo| `utxo.txId` }
      store.utxos = utxos.to_json

      log "received tx_id:", tx_id
      log "TX pushed!!!"

      callback.(tx_id)
    end
  end

  def received_utxo(message, callback)
    -> (utxo) do
      log "received UTXO:", utxo
      self.sign_and_broadcast.(message, utxo, callback)
    end
  end

  def op_return(message, callback)
    Blockchain.utxo @address, self.received_utxo(message, callback)
  end

  def store
    Native(`localStorage`)
  end

  # TODO: use require 'json'; JSON.parse; zepto get
  def hashes_convert(array)
    Array.new(array).map do |elem|
      Hash.new elem
    end
  end

end
<file_sep>$fg: #121
$bg: #dce
$bg2: #fff

@import "vendor/skeleton.sass"

header
  text-align: center
  img
    margin: 0 auto

@media (#{$bp-larger-than-mobile})
  header img
    width: 30%

@media (#{$bp-larger-than-phablet})
  header
    text-align: left
    img
      width: 90%

header h1
  margin-top: 5%

.container
  margin-top: 40px

input
  font-size: 110%

input[name=message]
  width: 100%

input[type=submit], button
  width: 100%

.spacer10
  height: 10px

.spacer30
  height: 30px

.spacer50
  height: 50px

.spacer300
  height: 300px

.align-right
  text-align: right
<file_sep>require 'bundler'
Bundler.require
Oj.default_options = { mode: :compat }

path = File.expand_path "../../", __FILE__
APP_PATH = path
<file_sep>require_relative 'bc_stylus.rb'

run BCPen.freeze.app
<file_sep># create an rb bundle

CONCAT_FILES = %w(
  config/env
  lib/modules/http
  lib/modules/rmodel
  lib/modules/debug_helpers
  lib/modules/ui_helpers
  lib/models/blockchain
  lib/models/private_key
  lib/models/bit_core
  lib/models/pen
  lib/models/wallet
  lib/models/hasher
  lib/comp/message_form
  lib/comp/file_form
  lib/comp/success
  lib/comp/bc_stylus
  app
)

BUNDLE = "bundle.rb"

# :concat, type: "rb", files: files, input_dir: "lib", output: "dist/bundle" do

guard :shell do
  watch /^(?!bundle)(.+)\.rb$/ do |m|
    puts "bundling........."
    bundle = []
    for file in CONCAT_FILES
      bundle << File.read("#{file}.rb")
    end
    File.open(BUNDLE, "w"){ |f| f.write bundle.join("\n") }
    puts "bundled!"
  end
end

guard :shell do
  watch %r{^#{BUNDLE}$} do |m|
    puts "building........."
    puts `ruby build.rb`
    puts "#{m[0]} changed, regenerated opal bundle"
    puts "built!"
    puts
    puts
  end
end

guard :sass, input: "style", output: "css"
<file_sep>class BCPen
  include React::Component

  def render
    div className: "bc_stylus" do
      present MessageForm
      # present FileForm # alpha
    end
  end
end
<file_sep>class HTTP
  def self.get(url, callback)
    `$.getJSON(url, function(data){
      console.log("GET", url)
      callback(data)
    })`
  end

  def self.post(url, params, callback)
    # console.log
    # url = "/test"
    `var success = function(data){
      console.log("POST", url)
      callback(data)
    }`

    `var data = {
      tx: params.tx
    }

    console.log(JSON.stringify(data))
    `

    `ajax = {
      contentType: 'application/json',
      data: JSON.stringify(data),
      dataType: 'json',
      processData: false,
      type: 'POST',
      success: success,
      url: url
    }`

    `$.ajax(ajax)`

    # `$.ajax(url, ajax, function(data){
    #   console.log("POST", url)
    #   callback(data)
    # })`
  end
end
<file_sep>class FileForm
  include React::Component
  extend DebugHelpers

  define_state(:submit_disabled)  { false }

  def hash_file
   Hasher.hash `document.querySelector("input[name=file]").files[0]`
   `console.log("hash file called!!!")`
  end

  def render
    div className: "message_input" do
      div className: "row" do
        div className: "five columns" do
          input name: "file", type: "file"
        end
        div className: "one columns" do
          button(disabled: self.submit_disabled) do
            "Write hash"
          end.on(:click){ hash_file }
        end
      end
    end
  end
end
<file_sep>TODO: idea for a form components plugin
---

string parse to html elements - node / js / opal mixin

    [] = checkbox (unchecked)

    [x] = checkbox (checked)

    () = radio buttons

etc..

api: `el("string")` - `el "string"` (opal)

the name its taken from the label, the collection name from the outher element
<file_sep># BlockchainPen

BlockchainPen it's an UI and API for writing messages to the blockchain.

It is built using Ruby Opal and Bitcore.js

### Running it

Install the dependencies:

    bundle

Open the project in a webserver:

    rake

or

    rackup

then open a browser at:

<http://localhost:3000>

#### Development

launch guard:

    guard

when you modify a ruby file the dist/bundle.js file will be transpiled

---

you can also run everything with a simple command (experimental)

    rake

- roda (routing tree framework)
- websocket plugin (uses faye)
- opal (write js in ruby)
- opal-browser (use websocket easily)
- react.rb (js dom diffing + cool ruby API to define views + state models)

the old name was BlockchainPen, all the files in the repo named blockchain_stylus or bc_stylus are main files :)
<file_sep>module RModel
  def attributes
    attrs = instance_variables.map{ |a| a.to_s[1..-1] }
    (attrs - ["constructor", "toString"]).map(&:to_sym)
  end
end
<file_sep>class Wallet
  include RModel

  attr_accessor :address, :balance

  def initialize(address:, balance:)
    @address = address
    @balance = balance
  end

  TEST = ->{ Wallet.new address: "1asd", balance: 10_000 }
end

# test
# w = Wallet::TEST.(); puts w.attributes
<file_sep>class Success
  include React::Component

  define_state(:tx_id) { "" }

  def render
    div className: "success" do
      p "Message written:"
      p self.tx_id
    end
  end
end
<file_sep>module DebugHelpers
  def log(*mex)
    if mex.size == 1
      `console.log(mex[0])`
    else
      `console.log(mex[0], mex[1])`
    end
  end
end
<file_sep>class Hasher

  def self.hash_file

  end

  def self.hash(file)

   `

    reader = new FileReader();
    reader.onload = function(data) {
      window.crypto.subtle.digest(
        {
            name: "SHA-256",
        },
        data
      )
      .then(function(hash){
        console.log(new Uint8Array(hash))
      })
      .catch(function(err){
          console.error(err)
      })
    }
    reader.readAsArrayBuffer(file)

    `
  end

end
<file_sep>class MessageForm
  include React::Component
  extend DebugHelpers
  extend UIHelpers
  include UIHelpers

  define_state(:chars)  { 0 }
  define_state(:submit_disabled)  { false }
  define_state(:tx_id)  { nil }
  define_state(:loading) { false }

  MAX_CHARS = 75

  def write
    log "writing message: #{self.message}"
    self.loading = true
    Pen.write self.message, self.callback_write
  end

  def message
    # mex = q "input[name=message]"
    `document.querySelector("input[name=message]").value`
  end

  def update_counter
    self.chars = String.new(message).size
    self.submit_disabled = true if self.chars > MAX_CHARS
  end

  def render
    div className: "message_input" do
      div className: "row align-right" do
        span do
          self.chars
        end
        span do
          " / #{MAX_CHARS} chars"
        end
      end
      spacer
      div className: "row" do
        div className: "five columns" do
          input(name: "message", placeholder: "your important message...", type: "text")
            .on(:change){ update_counter }
        end
        div className: "one columns" do
          button(disabled: self.submit_disabled) do
            "Write"
          end.on(:click){ write }
        end
      end
      div className: "spinner" do
        span { "loading..." }
      end if self.loading
      if self.tx_id
        div className: "row" do
          div className: "spacer30"
          div { "the message has been written: #{self.tx_id}" }
          div do
            span do
              a href: "https://live.blockcypher.com/btc/tx/#{self.tx_id}" do
                "blockcypher.com"
              end
            end
            span { " - " }
            span do
              a href: "https://blockchain.info/tx/#{self.tx_id}" do
                "blockchain.info"
              end
            end
            span { " - " }
            span do
              a href: "https://chain.so/tx/BTC/#{self.tx_id}" do
                "chain.so"
              end
            end
            span { " - " }
            span do
              a href: "http://eternitywall.it/m/#{self.tx_id}" do
                "eternitywall.it"
              end
            end
          end
        end
      end
    end
  end

  def callback_write
    -> (tx_id) do
      self.tx_id = tx_id
      self.loading = false
    end
  end

  def spacer
    div className: "spacer10"
  end
end
<file_sep>class Blockchain

  def self.utxo(address, callback)
    `console.log(address)`
    utxo_url = "https://blockchain.info/unspent?active=#{address}&format=json&cors=true"

    HTTP.get utxo_url, self.utxo_callback(callback)
  end

  def self.utxo_callback(callback)
    -> (utxo) do
      `callback(utxo.unspent_outputs)`
    end
  end

  def self.pushtx(tx_hash, callback)
    pushtx_url = "https://api.blockcypher.com/v1/btc/main/txs/push"
    post_params = { tx: tx_hash }.to_n
    # pushtx_url = "https://insight.bitpay.com/api/tx/send"
    # post_params = { rawtx: tx_hash }.to_n
    HTTP.post pushtx_url, post_params, self.pushtx_callback(callback)
  end

  def self.pushtx_callback(callback)
    -> (tx_info) do
      `console.log("TX INFO", tx_info)`
      `callback(tx_info.tx.hash)`
    end
  end

end
<file_sep>task default: :run

task :run do
  IO.popen "bundle exec guard"
  IO.popen "rackup -p 3001"
  sleep
end

languages: Markdown Sass Ruby


text:
<file_sep>using System;
using System.Text;
using FluentAssertions;
using Nasa.MarsMission.Rovers.Basic;
using Xunit;

namespace Nasa.MarsMission.Rovers.Test
{
    public class BasicFleetTest
    {
        /// <summary>
        /// Test the successfully issuing commands to the fleet
        /// </summary>
        /// <param name="commands"></param>
        /// <param name="expectedOutputLines"></param>
        /// <param name="roverCount"></param>
        [Theory]
        [InlineData(new[] {"10 10"}, new[] {""}, 0)]
        [InlineData(new[] {"20 20", "10 10 N"}, new[] {"10 10 N"}, 1)]
        [InlineData(new[] {"20 20", "10 10 N", "RMMM"}, new[] {"13 10 E"}, 1)]
        // i/o specified in the exercise
        [InlineData(
            new[] {"5 5", "1 2 N", "LMLMLMLMM", "3 3 E", "MMRMMRMRRM"},
            new[] {"1 3 N", "5 1 E"},
            2)]
        public void Receive_should_correctly_interpret_commands(
            string[] commands,
            string[] expectedOutputLines,
            int roverCount)
        {
            // arrange
            var fleet = new BasicFleet();
            var sb = new StringBuilder();

            foreach (var outputLine in expectedOutputLines)
            {
                sb.AppendLine(outputLine);
            }

            var expectedOutput = sb.ToString().Trim();

            // act
            fleet.Receive(commands);

            // assert
            fleet.Rovers.Count.Should().Be(roverCount);
            fleet.ToString().Should().Be(expectedOutput);
        }

        /// <summary>
        /// Test that the fleet correctly identifies out of bound states
        /// </summary>
        /// <param name="commands"></param>
        [Theory]
        [InlineData("10 10", "20 20")]
        [InlineData("20 20", "21 5")]
        [InlineData("20 20", "5 21")]
        [InlineData("20 20", "5 -5")]
        public void Deploy_should_throw_if_initial_position_out_of_bounds(
            params string[] commands)
        {
            // arrange
            var fleet = new BasicFleet();

            // act/assert
            Assert.Throws<ArgumentException>(()=> fleet.Receive(commands));
        }
    }
}<file_sep>using System;
using Nasa.MarsMission.Rovers.Basic.Helpers;

namespace Nasa.MarsMission.Rovers.Basic
{
    public class BasicRover : SurfaceRoverBase
    {
        protected override int[] Move(int magnitude)
        {
            // we could process negatives, but given the basic implementation, we'll simply throw
            if (magnitude < 1)
            {
                throw new ArgumentOutOfRangeException(
                    nameof(magnitude),
                    $"Magnitude should be greater than 0. Magnitude received: {magnitude}");
            }

            var vector = Status.Bearing switch
            {
                0 =>
                    // east
                    new[] {magnitude, 0},
                90 =>
                    // north
                    new[] {0, magnitude},
                180 =>
                    // west
                    new[] {-magnitude, 0},
                270 =>
                    // south
                    new[] {0, -magnitude},
                _ => throw new InvalidOperationException(
                    $"Current bearing does not correspond to a compass direction. Current bearing: {Bearing}")
            };

            Position[0] += vector[0];
            Position[1] += vector[1];

            return Position;
        }

        protected override int Rotate(int angleOfRotation)
        {
            if (angleOfRotation % 90 != 0)
            {
                throw new ArgumentOutOfRangeException(
                    nameof(angleOfRotation),
                    $"Angle of rotation must be multiple of 90. Angle of rotation: {angleOfRotation}");
            }

            return Bearing = OrientationHelpers.NormalizeAngle(Bearing + angleOfRotation);
        }
    }
}<file_sep>namespace Nasa.MarsMission.Rovers.Core.Description.Terrain
{
    /// <summary>
    /// Represents terrain for exploration
    /// </summary>
    /// <typeparam name="TRoverStatus">The type of rover status.</typeparam>
    public interface ITerrain<in TRoverStatus>
    {
        /// <summary>
        /// The dimensions of the boundary
        /// </summary>
        int[] Dimensions { get; set; }

        /// <summary>
        /// Determines if provided rover status is out of bounds
        /// </summary>
        /// <param name="position">The position to check.</param>
        /// <returns><c>true</c> if out of bounds, else <c>false</c></returns>
        bool IsOutOfBounds(TRoverStatus position);
    }
}<file_sep>namespace Nasa.MarsMission.Rovers.Airborne
{
    public class DroneStatus
    {
        public double[] Position { get; set; }

        public double Bearing { get; set; }

        public double Height { get; set; }

        public override string ToString()
        {
            return $"{string.Join(' ', Position)} {Height} {Bearing}";
        }
    }
}<file_sep>using Nasa.MarsMission.Rovers.Basic.Helpers;

namespace Nasa.MarsMission.Rovers.Basic
{
    public class RoverLocation
    {
        /// <summary>
        /// Coordinates [x,y] representing the rover's current position
        /// </summary>
        public int[] Position { get; set; } = {0, 0};

        /// <summary>
        /// Degree value representing rover's current bearing
        /// </summary>
        public int Bearing { get; set; }

        public override string ToString()
        {
            return $"{string.Join(' ', Position)} {OrientationHelpers.ConvertToCompassDirection(Bearing)}";
        }
    }
}<file_sep>namespace Nasa.MarsMission.Rovers.Core.Fleet
{
    /// <summary>
    /// An instruction to be issued to a fleet of rovers
    /// </summary>
    public class FleetInstruction
    {
        /// <summary>
        /// The type of instruction e.g. DeployRover
        /// </summary>
        public InstructionType Type { get; set; }

        /// <summary>
        /// The content of the instruction e.g. "0 1 S" for deployed rover status
        /// </summary>
        public string Content { get; set; }

        public override string ToString()
        {
            return $"Type: {Type}, Content: {Content}";
        }
    }
}<file_sep>using System.Collections.Generic;

namespace Nasa.MarsMission.Rovers.Core.Fleet
{
    /// <summary>
    /// Represents a fleet of deployed rovers
    /// </summary>
    /// <typeparam name="TRover">The type of deployed rover.</typeparam>
    public interface IDeployedFleet<out TRover> : IInputReceiver<IReadOnlyList<string>>
        where TRover : IInputReceiver<string>
    {
        /// <summary>
        /// The list of currently deployed rovers
        /// </summary>
        IReadOnlyCollection<TRover> Rovers { get; }
    }
}<file_sep># mars-rovers

I've created a small library- Nasa.MarsMission.Rovers- which
provides the base interfaces/classes for a Mars
rover mission. It is fairly generic, with the key base classes
accepting string input and leveraging
abstraction to delegate specific implementation
details- such as interpreting the command, processing/executing the
steps- to derived classes.

The idea is to allow for different types of rover,
more advanced and with different features. An
incomplete example can be seen in the Airborne project,
with the beginnings of an AirborneDrone implementation.

For the original exercise, I created the necessary implementations
in the Basic project. Here, a basic rover is implemented, whose
capabilities are limited to moving on a horizontal grid (corresponding
to the plateau). The BasicRover is limited to moving 1 unit, and 90 degree
turns i.e. left/right. But a less limited surface rover could even be derived
from the SurfaceRoverBase abstract class. For example, one able to move a
variable units, turn 180, move orthogonal to its direction, or move diagonally.

The Basic code is exercised via a couple of xUnit tests in the Test project.
Here you can see the Rover and the Fleet respectively processing viable input, and
produced the correct output, as required in the challenge. The specific input/output
scenario specified in the test is marked with a comment, in the BasicFleetTest class.

## To build and run the code

This is a .NET library, to build it open the solution in Visual Studio
(or similar IDE e.g. Jetbrains Rider) and build. Or you can build from the
command line via running "dotnet build" in the solution directory.

To run the unit tests, either in an open IDE using the integrated test
UI or via the command line with "dotnet test" in the solution directory.

You should have .NET Core installed, normally this will come as an option
on Visual Studio. Otherwise can be downloaded here:

https://dotnet.microsoft.com/download/dotnet/3.1

## What could come next?

While completing the exercise, I considered some
fun and interesting ways of building upon this code, to
create something more engaging and interactive.

To give an overview of where I'd take this project:
- .NET Core WebApi to process, create fleets, process
  movement commands and return output etc. via this library
- Database to store information on current missions,
  fleets, rovers and their positions
- Mongo data access layer - used by the WebApi,
  to CRUD saved states etc.
- React UI to visualize the positions of the rovers. Start
  simple with a pixel grid, but could evolve.
- More advanced Rover types e.g. airborne drones,
navigation of uneven terrain, obstacles etc.<file_sep>namespace Nasa.MarsMission.Rovers.Core.Fleet
{
    /// <summary>
    /// Possible types of instruction for a fleet of rovers
    /// </summary>
    public enum InstructionType
    {
        /// <summary>
        /// Specify the terrain for exploration
        /// </summary>
        SetTerrain,

        /// <summary>
        /// Deploy a rover to the environment
        /// </summary>
        DeployRover,

        /// <summary>
        /// Issue a deployed rover with an instruction
        /// </summary>
        InstructRover
    }
}<file_sep>using System;
using Nasa.MarsMission.Rovers.Core.Description.Terrain;

namespace Nasa.MarsMission.Rovers.Airborne
{
    public class DroneAirspace : ITerrain<DroneStatus>
    {
        public int[] Dimensions { get; set; }

        public bool IsOutOfBounds(DroneStatus position)
        {
            // TODO: ensure the drone is not flying too high,
            // or too low over rocky terrain
            throw new NotImplementedException();
        }

        public override string ToString()
        {
            return $"Dimensions: ({string.Join(',', Dimensions)})";
        }
    }
}<file_sep>using System;
using System.Collections.Generic;
using System.Linq;

namespace Nasa.MarsMission.Rovers.Basic.Helpers
{
    public static class OrientationHelpers
    {
        private static readonly Dictionary<string, int> CompassDirectionDictionary = new Dictionary<string, int>
        {
            {"E", 0},
            {"N", 90},
            {"W", 180},
            {"S", 270}
        };

        public static int ConvertFromCompassDirection(string direction)
        {
            if (!CompassDirectionDictionary.TryGetValue(direction, out var bearing))
            {
                throw new ArgumentOutOfRangeException(nameof(direction));
            }

            return bearing;
        }

        public static string ConvertToCompassDirection(int bearing)
        {
            var direction = CompassDirectionDictionary
                .FirstOrDefault(e => e.Value == bearing)
                .Key;
            if (direction == null)
            {
                throw new ArgumentOutOfRangeException(nameof(bearing));
            }

            return direction;
        }

        public static int NormalizeAngle(int angle)
        {
            // normalize to -359 to 359
            angle %= 360;

            // if its negative, add 360 to give the coterminal angle (equiv. between 0 and 359)
            if (angle < 0)
            {
                angle += 360;
            }

            return angle;
        }
    }
}<file_sep>using System;
using System.Collections.Generic;
using Nasa.MarsMission.Rovers.Core.Agent;

namespace Nasa.MarsMission.Rovers.Airborne
{
    public class AirborneDrone : DeployedRoverBase<DroneStatus, DroneAction>
    {
        protected override IEnumerable<DroneAction> Interpret(string input)
        {
            // TODO: interpret input e.g. "V10 R360 V-10"
            // list off 10m above ground, rotate once, land
            return Array.Empty<DroneAction>();
        }

        protected override void Execute(DroneAction action)
        {
            switch (action.Type)
            {
                case ActionType.Horizontal:
                    // TODO: move horizontally
                    break;
                case ActionType.Vertical:
                    // TODO: ascend/descend
                case ActionType.Rotate:
                    // TODO: rotate
                    break;
                default:
                    throw new ArgumentOutOfRangeException(
                        nameof(action.Type),
                        $"Unrecognized rover action type: {action.Type}");
            }
        }
    }
}<file_sep>using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using Nasa.MarsMission.Rovers.Core.Agent;
using Nasa.MarsMission.Rovers.Core.Description.Terrain;

namespace Nasa.MarsMission.Rovers.Core.Fleet
{
    /// <summary>
    /// A deployed fleet of rovers
    /// </summary>
    /// <typeparam name="TRover">The type of rover.</typeparam>
    /// <typeparam name="TStatus">The type of rover status.</typeparam>
    /// <typeparam name="TTerrain">The type of terrain.</typeparam>
    public abstract class DeployedFleetBase<TRover, TStatus, TTerrain>
        : InputReceiverBase<FleetInstruction, IReadOnlyList<string>>, IDeployedFleet<TRover>
        where TRover : IDeployedRover<TStatus>
        where TTerrain : ITerrain<TStatus>
    {
        private readonly List<TRover> _rovers = new List<TRover>();

        public IReadOnlyCollection<TRover> Rovers => _rovers;

        /// <summary>
        /// The pattern for valid Deploy commands
        /// </summary>
        protected abstract Regex DeployRoverPattern { get; }

        /// <summary>
        /// The pattern for valid Instruct commands
        /// </summary>
        protected abstract Regex InstructRoverPattern { get; }

        private TRover ActiveRover => GetActiveRover();
        private TTerrain Terrain { get; set; }

        /// <summary>
        /// Interprets input as a sequence of fleet instructions
        /// </summary>
        /// <param name="input">The input.</param>
        /// <returns>The fleet instructions.</returns>
        /// <exception cref="ArgumentException"></exception>
        protected override IEnumerable<FleetInstruction> Interpret(
            IReadOnlyList<string> input)
        {
            for (var i = 0; i < input.Count; i++)
            {
                var inputLine = input[i];

                // the first command will always be to set the terrain
                if (i == 0)
                {
                    yield return new FleetInstruction
                    {
                        Type = InstructionType.SetTerrain,
                        Content = inputLine
                    };
                }
                else if (DeployRoverPattern.IsMatch(inputLine))
                {
                    yield return new FleetInstruction
                    {
                        Type = InstructionType.DeployRover,
                        Content = inputLine
                    };
                }
                else if (InstructRoverPattern.IsMatch(inputLine))
                {
                    yield return new FleetInstruction
                    {
                        Type = InstructionType.InstructRover,
                        Content = inputLine
                    };
                }
                else if (!string.IsNullOrEmpty(inputLine)) // empty string is fine to ignore
                {
                    throw new ArgumentException($"Unable to interpret line {i} of input. Line: {inputLine}");
                }
            }
        }

        /// <summary>
        /// Executes a fleet instruction
        /// </summary>
        /// <param name="instruction">The instruction.</param>
        /// <exception cref="InvalidOperationException"></exception>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        protected override void Execute(FleetInstruction instruction)
        {
            if (instruction.Type != InstructionType.SetTerrain)
            {
                if (Terrain == null)
                {
                    throw new InvalidOperationException(
                        $"Unable to execute instruction: {instruction}. Terrain has not been set");
                }
            }

            switch (instruction.Type)
            {
                case InstructionType.SetTerrain:
                    // extract the terrain from the instruction
                    Terrain = ExtractTerrain(instruction.Content);
                    break;
                case InstructionType.DeployRover:
                    DeployRover(instruction.Content);
                    break;
                case InstructionType.InstructRover:
                    InstructRover(instruction.Content);
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }

        /// <summary>
        /// Get an instance of a rover for deployment
        /// </summary>
        /// <param name="roverStatus"></param>
        /// <returns>A rover instance.</returns>
        protected abstract TRover GetRoverInstance(TStatus roverStatus);

        /// <summary>
        /// Gets the active rover, to be instructed.
        /// </summary>
        /// <returns>The active rover instance.</returns>
        protected abstract TRover GetActiveRover();

        /// <summary>
        /// Extracts the rover status from the input
        /// </summary>
        /// <param name="input">The input.</param>
        /// <returns>The extracted status.</returns>
        protected abstract TStatus ExtractRoverStatus(string input);

        /// <summary>
        /// Extracts the terrain spec from the input
        /// </summary>
        /// <param name="input">The input.</param>
        /// <returns>The terrain spec.</returns>
        protected abstract TTerrain ExtractTerrain(string input);

        /// <summary>
        /// Deploy a rover to the environment
        /// </summary>
        /// <param name="instruction">The instruction corresponding to the initial rover status.</param>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        private void DeployRover(string instruction)
        {
            // extract the initial rover status from the instruction
            var roverStatus = ExtractRoverStatus(instruction);

            // check the initial status isn't out of bounds
            if (Terrain.IsOutOfBounds(roverStatus))
            {
                throw new ArgumentOutOfRangeException(
                    nameof(roverStatus),
                    $"The initial rover status is out of the bounds of the terrain. Status: {roverStatus}");
            }

            // create a new rover and add it to the collection
            _rovers.Add(GetRoverInstance(roverStatus));
        }

        /// <summary>
        /// Issues an instruction to the active rover
        /// </summary>
        /// <param name="instruction">The instruction.</param>
        /// <exception cref="InvalidOperationException"></exception>
        private void InstructRover(string instruction)
        {
            if (Rovers.Count < 1)
            {
                throw new InvalidOperationException(
                    "There are no rovers deployed.");
            }

            var rover = ActiveRover;

            if (rover == null)
            {
                throw new InvalidOperationException(
                    "There are no active rovers");
            }

            rover.Receive(instruction);

            if (Terrain.IsOutOfBounds(rover.Status))
            {
                throw new InvalidOperationException(
                    "The specified command moves the rover to a position out of the bounds of the terrain.");
            }

            rover.CommandsProcessed++;
            rover.Ready = false;
        }

        public override string ToString()
        {
            var sb = new StringBuilder();

            foreach (var rover in Rovers)
            {
                sb.AppendLine(rover.Status.ToString());
            }

            return sb.ToString().Trim();
        }
    }
}<file_sep>namespace Nasa.MarsMission.Rovers.Basic
{
    /// <summary>
    /// Possible types of action for a rover
    /// </summary>
    public enum ActionType
    {
        /// <summary>
        /// Move some specified magnitude on current bearing
        /// </summary>
        Move,

        /// <summary>
        /// Rotate by a specified angle of rotation
        /// </summary>
        Rotate,

        /// <summary>
        /// Travel vertically
        /// </summary>
        Vertical
    }
}<file_sep>using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using Nasa.MarsMission.Rovers.Basic.Helpers;
using Nasa.MarsMission.Rovers.Core.Fleet;

namespace Nasa.MarsMission.Rovers.Basic
{
    public class BasicFleet : DeployedFleetBase<BasicRover, RoverLocation, BasicGridTerrain>
    {
        protected override Regex DeployRoverPattern { get; } = new Regex(@"^\d+ \d+ [NESW]$");
        protected override Regex InstructRoverPattern { get; } = new Regex(@"^[LRM]+$");

        protected override BasicRover GetRoverInstance(RoverLocation roverStatus)
        {
            return new BasicRover
            {
                Ready = true,
                Status = roverStatus,
                Designation = Guid.NewGuid().ToString()
            };
        }

        protected override BasicRover GetActiveRover()
        {
            return Rovers.FirstOrDefault(r => r.Ready);
        }

        protected override RoverLocation ExtractRoverStatus(string input)
        {
            // input should be 2 numbers for location, 1 for bearing e.g. "1 5 S"
            var elements = input.Split(' ');

            if (elements.Length != 3)
            {
                throw new ArgumentException(
                    $"Input is invalid for rover status. Input: {input}",
                    nameof(input));
            }

            var positionElements = elements.Take(2);
            var position = ExtractPosition(positionElements.ToArray());

            var directionElement = elements.Skip(2).SingleOrDefault();

            if (directionElement == null || directionElement.Length != 1)
            {
                throw new ArgumentException(
                    $"Input is invalid for rover status. Input: {input}",
                    nameof(input));
            }

            return new RoverLocation
            {
                Position = position,
                Bearing = OrientationHelpers.ConvertFromCompassDirection(directionElement)
            };
        }

        protected override BasicGridTerrain ExtractTerrain(string input)
        {
            // input should be 2 numbers, corresponding to a map point e.g. "5 5"
            var elements = input.Split(' ');

            if (elements.Length != 2)
            {
                throw new ArgumentException(
                    $"Input is invalid for rover status. Input: {input}",
                    nameof(input));
            }

            return new BasicGridTerrain
            {
                Dimensions = ExtractPosition(elements)
            };
        }

        private static int[] ExtractPosition(IReadOnlyList<string> positionElements)
        {
            var position = new int[2];
            for (var i = 0; i < 2; i++)
            {
                var positionElement = positionElements[i];
                if (int.TryParse(positionElement, out var value))
                {
                    position[i] = value;
                }
                else
                {
                    throw new ArgumentException(
                        $"Failed to convert position element to numeric value. Position element: {positionElement}");
                }
            }

            return position;
        }
    }
}<file_sep>using FluentAssertions;
using Nasa.MarsMission.Rovers.Basic;
using Xunit;

namespace Nasa.MarsMission.Rovers.Test
{
    public class BasicRoverTest
    {
        /// <summary>
        /// Test sending a load of instructions to a rover instance
        /// </summary>
        /// <param name="initialBearing"></param>
        /// <param name="initialPosition"></param>
        /// <param name="input"></param>
        /// <param name="expectedFinalBearing"></param>
        /// <param name="expectedFinalPosition"></param>
        [Theory]
        // move
        [InlineData(0, new[] {0, 0}, "M", 0, new[] {1, 0})]
        [InlineData(0, new[] {0, 0}, "MMM", 0, new[] {3, 0})]
        [InlineData(90, new[] {3, 4}, "MMM", 90, new[] {3, 7})]
        [InlineData(180, new[] {10, 4}, "MMMMM", 180, new[] {5, 4})]
        [InlineData(270, new[] {10, 4}, "MMM", 270, new[] {10, 1})]
        // rotate
        [InlineData(0, new[] {5, 7}, "L", 90, new[] {5, 7})]
        [InlineData(0, new[] {5, 7}, "R", 270, new[] {5, 7})]
        [InlineData(270, new[] {5, 7}, "L", 0, new[] {5, 7})]
        [InlineData(180, new[] {5, 7}, "R", 90, new[] {5, 7})]
        [InlineData(180, new[] {5, 7}, "LLL", 90, new[] {5, 7})]
        [InlineData(270, new[] {5, 7}, "LLL", 180, new[] {5, 7})]
        [InlineData(270, new[] {5, 7}, "RRRR", 270, new[] {5, 7})]
        // combined
        [InlineData(0, new[] {0, 0}, "LMMRMM", 0, new[] {2, 2})]
        [InlineData(0, new[] {0, 0}, "MMMMMRMMRMM", 180, new[] {3, -2})]
        [InlineData(270, new[] {10, 5}, "MMLMMRMMMMRMM", 180, new[] {10, -1})]
        public void Receive_should_update_to_the_correct_state_given_initial_state(
            int initialBearing,
            int[] initialPosition,
            string input,
            int expectedFinalBearing,
            int[] expectedFinalPosition
        )
        {
            // arrange
            var status = new RoverLocation
            {
                Bearing = initialBearing,
                Position = initialPosition
            };
            var rover = new BasicRover
            {
                Status = status
            };

            // act
            rover.Receive(input);

            // assert
            rover.Status.Position.Should().Equal(expectedFinalPosition);
            rover.Status.Bearing.Should().Be(expectedFinalBearing);
        }
    }
}<file_sep>namespace Nasa.MarsMission.Rovers.Core
{
    /// <summary>
    /// Represents an entity which will receive input
    /// </summary>
    /// <typeparam name="TInput">The type of input.</typeparam>
    public interface IInputReceiver<in TInput>
    {
        /// <summary>
        /// Receives input and attempts to process it
        /// </summary>
        /// <param name="input">The input object.</param>
        /// <returns>The receiver for chaining.</returns>
        IInputReceiver<TInput> Receive(TInput input);
    }
}<file_sep>using System.Collections.Generic;

namespace Nasa.MarsMission.Rovers.Core
{
    /// <summary>
    /// An entity that will receive and interpret input,
    /// subsequently executing the interpreted actions
    /// </summary>
    /// <typeparam name="TExecutable">The type of executable, usually an action to perform.</typeparam>
    /// <typeparam name="TInput">The type of input.</typeparam>
    public abstract class InputReceiverBase<TExecutable, TInput> : IInputReceiver<TInput>
    {
        /// <summary>
        /// Receives input and attempts to process it,
        /// interpreting then executing the interpreted actions
        /// </summary>
        /// <param name="input">The input object.</param>
        /// <returns>The receiver for chaining.</returns>
        public IInputReceiver<TInput> Receive(TInput input)
        {
            var actions = Interpret(input);

            foreach (var action in actions)
            {
                Execute(action);
            }

            return this;
        }

        /// <summary>
        /// Interprets input as a sequence of executable segments
        /// </summary>
        /// <param name="input">The input object.</param>
        /// <returns>The executable segments.</returns>
        protected abstract IEnumerable<TExecutable> Interpret(TInput input);

        /// <summary>
        /// Executes a defined segment of input
        /// </summary>
        /// <param name="executable">The action object.</param>
        protected abstract void Execute(TExecutable executable);
    }
}<file_sep>namespace Nasa.MarsMission.Rovers.Basic
{
    /// <summary>
    /// Represents a discrete action for a rover to perform
    /// </summary>
    public class RoverAction
    {
        /// <summary>
        /// The relevant value e.g. a magnitude
        /// </summary>
        public int Value { get; set; }

        /// <summary>
        /// The type of action to perform e.g. Move
        /// </summary>
        public ActionType Type { get; set; }
    }
}<file_sep>using System;
using System.Collections.Generic;
using System.Linq;
using Nasa.MarsMission.Rovers.Basic.Helpers;
using Nasa.MarsMission.Rovers.Core.Agent;

namespace Nasa.MarsMission.Rovers.Basic
{
    public abstract class SurfaceRoverBase : DeployedRoverBase<RoverLocation, RoverAction>
    {
        private static readonly char[] PermittedChars = {'M', 'R', 'L'};
        private readonly RoverLocation _status = new RoverLocation();

        protected override IEnumerable<RoverAction> Interpret(string input)
        {
            var steps = input.ToCharArray();

            if (!steps.All(s => PermittedChars.Contains(s)))
            {
                throw new ArgumentException(
                    $"Command string must contain only 'M', 'L', 'R'. Command received: {input}");
            }

            foreach (var step in steps)
            {
                var action = step switch
                {
                    'L' => new RoverAction {Type = ActionType.Rotate, Value = 90},
                    'R' => new RoverAction {Type = ActionType.Rotate, Value = -90},
                    'M' => new RoverAction {Type = ActionType.Move, Value = 1},
                    _ => throw new ArgumentException(
                        $"Command string must contain only 'M', 'L', 'R'. Command received: {input}")
                };

                yield return action;
            }
        }

        protected override void Execute(RoverAction action)
        {
            switch (action.Type)
            {
                case ActionType.Move:
                    Move(action.Value);
                    break;
                case ActionType.Rotate:
                    Rotate(action.Value);
                    break;
                default:
                    throw new ArgumentOutOfRangeException(
                        nameof(action.Type),
                        $"Unrecognized rover action type: {action.Type}");
            }
        }

        protected abstract int[] Move(int magnitude);
        protected abstract int Rotate(int angleOfRotation);

        public override RoverLocation Status
        {
            get => _status;
            set
            {
                Bearing = value.Bearing;
                Position = value.Position;
            }
        }

        protected int Bearing
        {
            get => _status.Bearing;
            // ensure bearing is 0 - 360
            set => _status.Bearing = OrientationHelpers.NormalizeAngle(value);
        }

        protected int[] Position
        {
            get => _status.Position;
            set => _status.Position = value;
        }
    }
}<file_sep>using Nasa.MarsMission.Rovers.Core.Description;

namespace Nasa.MarsMission.Rovers.Core.Agent
{
    /// <summary>
    /// Represents a rover deployed to an environment
    /// </summary>
    /// <typeparam name="TStatus">The rover status.</typeparam>
    public interface IDeployedRover<TStatus> : IInputReceiver<string>, IRoverDetails
    {
        /// <summary>
        /// The current status of the rover e.g. containing positional information
        /// </summary>
        TStatus Status { get; set; }
    }
}<file_sep>using System;
using System.Linq;
using Nasa.MarsMission.Rovers.Core.Description.Terrain;

namespace Nasa.MarsMission.Rovers.Basic
{
    public class BasicGridTerrain : ITerrain<RoverLocation>
    {
        private int[] _dimensions;

        public int[] Dimensions
        {
            get => _dimensions;
            set
            {
                if (value == null)
                {
                    throw new ArgumentNullException(nameof(value));
                }

                if (value.Length < 2 || value.Length > 3)
                {
                    throw new ArgumentException(
                        "Number of dimensions must be 2 or 3",
                        nameof(value));
                }

                if (value.Any(d => d < 1))
                {
                    throw new ArgumentException(
                        "All boundary values must be greater than 0",
                        nameof(value));
                }

                _dimensions = value;
            }
        }

        public bool IsOutOfBounds(RoverLocation roverStatus)
        {
            if (Dimensions == null)
            {
                throw new InvalidOperationException(
                    "The dimensions are not set on the terrain.");
            }

            var position = roverStatus.Position;

            if (position == null)
            {
                throw new ArgumentNullException(nameof(position));
            }

            if (position.Length != Dimensions.Length)
            {
                throw new ArgumentException(
                    $"The number of positional dimensions must be {Dimensions.Length}. Received: {position.Length}",
                    nameof(position));
            }

            // ReSharper disable once LoopCanBeConvertedToQuery
            for (var i = 0; i < position.Length; i++)
            {
                if (position[i] < 0 || position[i] > Dimensions[i])
                {
                    return true;
                }
            }

            return false;
        }

        public override string ToString()
        {
            return $"Dimensions: ({string.Join(',', Dimensions)})";
        }
    }
}<file_sep>using System.Text.RegularExpressions;
using Nasa.MarsMission.Rovers.Core.Fleet;

namespace Nasa.MarsMission.Rovers.Airborne
{
    public class AirborneFleet : DeployedFleetBase<AirborneDrone, DroneStatus, DroneAirspace>
    {
        protected override Regex DeployRoverPattern { get; } = new(@"^\d+ \d+ [0-360]{1}$");
        protected override Regex InstructRoverPattern { get; }

        protected override AirborneDrone GetRoverInstance(DroneStatus roverStatus)
        {
            // TODO: create a new drone instance
            // this could be delegated to a factory function passed to the constructor
            throw new System.NotImplementedException();
        }

        protected override AirborneDrone GetActiveRover()
        {
            // TODO: this will likely be similar, but could
            // also be predicated on whether the rover is in flight,
            // or out of solar or something...
            throw new System.NotImplementedException();
        }

        protected override DroneStatus ExtractRoverStatus(string input)
        {
            // TODO: pretty straight forward, same as the other one
            // but also pull out height and now bearing/position
            // are continuous
            throw new System.NotImplementedException();
        }

        protected override DroneAirspace ExtractTerrain(string input)
        {
            // TODO: extract the terrain, for example, this would contain
            // the horizontal boundaries, max height and could include
            // any rocky terrain you wouldn't want the drone flying too low over
            // or landing in
            throw new System.NotImplementedException();
        }
    }
}<file_sep>using System;
using System.Text;

namespace Nasa.MarsMission.Rovers.Core.Agent
{
    /// <summary>
    /// A rover deployed to an environment
    /// </summary>
    /// <typeparam name="TStatus">The type of rover status.</typeparam>
    /// <typeparam name="TAction">The type of rover action.</typeparam>
    public abstract class DeployedRoverBase<TStatus, TAction>
        : InputReceiverBase<TAction, string>, IDeployedRover<TStatus>
    {
        /// <summary>
        /// The unique name of the rover
        /// </summary>
        public string Designation { get; set; } = Guid.NewGuid().ToString();

        /// <summary>
        /// Whether rover is ready to receive commands
        /// </summary>
        public bool Ready { get; set; }

        /// <summary>
        /// The number of commands processed by this rover
        /// </summary>
        public int CommandsProcessed { get; set; }

        /// <summary>
        /// The current status of the rover
        /// </summary>
        public virtual TStatus Status { get; set; }

        public override string ToString()
        {
            return new StringBuilder($"Rover ${Designation}")
                .AppendLine($"Status: {Status}")
                .AppendLine($"Commands processed: {CommandsProcessed}")
                .AppendLine($"Ready: {Ready}").ToString();
        }
    }
}<file_sep>namespace Nasa.MarsMission.Rovers.Core.Description
{
    /// <summary>
    /// Represents the inherent details of a rover
    /// </summary>
    public interface IRoverDetails
    {
        /// <summary>
        /// The unique name of the rover
        /// </summary>
        string Designation { get; set; }

        /// <summary>
        /// Whether rover is ready to receive commands
        /// </summary>
        bool Ready { get; set; }

        /// <summary>
        /// The number of commands processed by this rover
        /// </summary>
        int CommandsProcessed { get; set; }
    }
}<file_sep>namespace Nasa.MarsMission.Rovers.Airborne
{
    /// <summary>
    /// Represents a discrete action for a drone to perform
    /// </summary>
    public class DroneAction
    {
        /// <summary>
        /// The relevant value e.g. a magnitude
        /// </summary>
        public double Value { get; set; }

        /// <summary>
        /// The type of action to perform e.g. Move
        /// </summary>
        public ActionType Type { get; set; }
    }
}<file_sep>namespace Nasa.MarsMission.Rovers.Airborne
{
    /// <summary>
    /// Possible types of action for a rover
    /// </summary>
    public enum ActionType
    {
        /// <summary>
        /// Move some specified magnitude on current bearing
        /// </summary>
        Horizontal,

        /// <summary>
        /// Travel vertically
        /// </summary>
        Vertical,

        /// <summary>
        /// Rotate by a specified angle of rotation
        /// </summary>
        Rotate
    }
}

languages: C# Markdown

repo_name: zucchinho/mars-rovers