Skip to content

Commit

Permalink
started to write new README
Browse files Browse the repository at this point in the history
removed a bug regarding width calc of the NewDrawer in NodeString.Draw()
  • Loading branch information
m1gwings committed Sep 1, 2020
1 parent a834b72 commit 660a6f1
Show file tree
Hide file tree
Showing 4 changed files with 207 additions and 77 deletions.
226 changes: 152 additions & 74 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,96 +1,174 @@
# treedrawer
treedrawer is a simple go module that will help you to visualize binary trees in the command line.
# Motivation
I started to build this module by trying to solve an exercise on "The Go Programming Language". The task was to implement the String() function of a binary tree.
# Code structure
This module has two sub-packages:
* treedrawer/**tree**
* treedrawer/**drawer**

**tree** provides a simple representation of a binary tree:
**treedrawer** is a Go module that will help you drawing trees to console like the one below.
```
╭─╮
│9│
╰┬╯
╭─────────────────┴─────────────┬─────────────┬───┬───╮
╭──────────┴─────────╮ ╭┴╮ ╭┴╮ ╭┴╮ ╭┴╮
│I can handle strings│ │1│ │2│ │3│ │4│
╰──────────┬─────────╯ ╰┬╯ ╰─╯ ╰─╯ ╰─╯
│ ╭─────┬──┴─────╮
╭────────────────┴────────────────╮ ╭─┴─╮ ╭─┴╮ ╭────┴───╮
│with as many children as you want│ │124│ │13│ │a string│
╰────────────────┬────────────────╯ ╰───╯ ╰──╯ ╰────────╯
╭───────────────┴───────────────╮
│with as many layers as you want│
╰───────────────┬───────────────╯
╭─────────────────┴─────────────────╮
│actually I can handle everything...│
╰─────────────────┬─────────────────╯
╭──────────────────┴──────────────────╮
│...that satisfies NodeValue interface│
╰─────────────────────────────────────╯
```
## Import
```go
import "github.com/m1gwings/treedrawer/tree"
```
// Tree describes the node of a tree with atmost two children.
type Tree struct {
val int64
left, right, parent *Tree
}
## Quick start
```sh
# Assume the following code is in example.go file
$ cat example.go
```
```go
package main

**drawer** is a very simple "unicode-canvas" on which you can draw runes representing unicode characters or another whole canvas specifying the coordinates of the upper-left corner.
import (
"fmt"

---
If you run the following in the root directory:
```
go build .
```
"github.com/m1gwings/treedrawer/tree"
)

you will get a binary that prints random trees to give you an idea.
```
./treedrawer
func main() {
// Creating a tree with 5 as the value of the root node
t := tree.NewTree(tree.NodeInt64(5))

14
37
╭────┴────╮
97 29
│ ╭──┴──╮
12 47 24
// Adding children
t.AddChild(tree.NodeString("adding a string"))
t.AddChild(tree.NodeInt64(4))
t.AddChild(tree.NodeInt64(3))

// Drawing the tree
fmt.Println(t)
}
```
```sh
$ go run example.go
```
```sh
╭─╮
│5│
╰┬╯
╭───┴──────┬───╮
╭───────┴───────╮ ╭┴╮ ╭┴╮
│adding a string│ │4│ │3│
╰───────────────╯ ╰─╯ ╰─╯

With the **-l** flag you can specify the maximum number of layers of the random tree
```
./treedrawer -l 3
## Usage
### Building the tree
Creating the tree with 1 as the value of the root node
```go
t := tree.NewTree(tree.NodeInt64(1))
```
Adding the first child to t with value 2
```go
t.AddChild(tree.NodeInt64(2))
```
Adding more children
```go
t.AddChild(tree.NodeInt64(3))
t.AddChild(tree.NodeInt64(4))
t.AddChild(tree.NodeInt64(5))
```
We've just built the tree below
```
╭─╮
│1│
╰┬╯
╭───┬─┴─┬───╮
╭┴╮ ╭┴╮ ╭┴╮ ╭┴╮
│2│ │3│ │4│ │5│
╰─╯ ╰─╯ ╰─╯ ╰─╯
71
60
╭──┴──╮
45 51
```
# Import
In order to use the tree API below import the following:
### Navigating the tree
Navigating to first child of t (we're still working with the tree above)
```go
// This method returns an error if the i-th child does not exist
// in this case i = 0
tFirstChild, err := t.Child(0)
```
Adding children to first child
```go
tFirstChild.AddChild(tree.NodeInt64(6))
tFirstChild.AddChild(tree.NodeInt64(7))
tFirstChild.AddChild(tree.NodeInt64(8))
```
Going back to parent
```go
// ok would be equal to false if tFirstChild were the root of the tree
tFirstChildParent, ok := tFirstChild.Parent()

_ := tFirstChildParent == t // true, we have gone back to the root of the tree
```
import "github.com/m1gwings/treedrawer/tree"
Navigating to third child of t
```go
tThirdChild, err := t.Child(2)
```
# API
## Building the tree
### func NewTree(val int64) *Tree
NewTree is the default constructor for Tree.
Adding a string child to third child
```go
tThirdChild.AddChild(tree.NodeString("I'm a string"))
```
t := tree.NewTree(2)
Getting a pointer to the root of the tree
```go
tRoot := tThirdChild.Root()

_ := tRoot == t // true
```
Now the tree looks like this
```
╭─╮
│1│
╰┬╯
╭───────┬───┴─────┬─────────╮
╭┴╮ ╭┴╮ ╭┴╮ ╭┴╮
│2│ │3│ │4│ │5│
╰┬╯ ╰─╯ ╰┬╯ ╰─╯
╭───┼───╮ │
╭┴╮ ╭┴╮ ╭┴╮ ╭──────┴─────╮
│6│ │7│ │8│ │I'm a string│
╰─╯ ╰─╯ ╰─╯ ╰────────────╯
```
### func (t *Tree) AddLeft(val int64)
AddLeft adds a left child to the current node which will held val.
### Getting and setting values from the tree
Getting the value of a node
```go
v := t.Val()
```
t.AddLeft(5)
Setting the value of a node
```go
t.SetVal(tree.NodeInt64(3))
```
### func (t *Tree) AddRight(val int64)
AddRight adds a right child to the current node which will held val.
### Drawing the tree
*tree.Tree implements the Stringer interface, just use fmt to draw trees to console
```go
fmt.Println(t)
```
t.AddRight(3)
### Implementing NodeValue interface
## Examples
## Known issues
- Emojis are larger than normal characters
```go
fmt.Println(tree.NewTree(tree.NodeString("emojis are buggy 🤪")))
```
## Printing the tree
Tree type satisfies the Stringer interface, you can easily use fmt package to get results in the console.
```
fmt.Println(t)
╭──────────────────╮
│emojis are buggy 🤪│
╰──────────────────╯
2
╭─┴─╮
5 3
```
## Retreiving values from the tree
### func (t *Tree) Val() int64
Val returns the value held by the current node of the tree.
## Navigating the tree
### func (t *Tree) Left() (ok bool)
Left moves the current node to its left child.
Returns false if there is no left child, otherwise it returns true.
### func (t *Tree) Right() (ok bool)
Right moves the current node to its right child.
Returns false if there is no right child, otherwise it returns true.
### func (t *Tree) Parent() (ok bool)
Parent moves the current node to its parent.
Returns false if this node is the root of the whole tree, otherwise it returns true.
## Extra
### func Rand(n int) *Tree
Rand returns the root of a random three with at most n layers.
```
49 changes: 49 additions & 0 deletions tree/examples_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package tree

import (
"fmt"
"log"
"testing"
)

func TestShowcase(t *testing.T) {
tr := NewTree(NodeInt64(9))
tr.AddChild(NodeString("I can handle strings"))
tr.AddChild(NodeInt64(1))
tr.AddChild(NodeInt64(2))
tr.AddChild(NodeInt64(3))
tr.AddChild(NodeInt64(4))
tr, err := tr.Child(1)
if err != nil {
log.Fatal(err)
}
tr.AddChild(NodeInt64(124))
tr.AddChild(NodeInt64(13))
tr.AddChild(NodeString("a string"))
tr, ok := tr.Parent()
if !ok {
log.Fatal(fmt.Errorf("this child should have a parent"))
}
tr, err = tr.Child(0)
if err != nil {
log.Fatal(err)
}
tr.AddChild(NodeString("with as many children as you want"))
tr, err = tr.Child(0)
if err != nil {
log.Fatal(err)
}
tr.AddChild(NodeString("with as many layers as you want"))
tr, err = tr.Child(0)
if err != nil {
log.Fatal(err)
}
tr.AddChild(NodeString("actually I can handle everything..."))
tr, err = tr.Child(0)
if err != nil {
log.Fatal(err)
}
tr.AddChild(NodeString("...that satisfies NodeValue interface"))

fmt.Println(tr)
}
7 changes: 5 additions & 2 deletions tree/nodevalues.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"log"
"strconv"
"unicode/utf8"

"github.com/m1gwings/treedrawer/drawer"
)
Expand All @@ -29,15 +30,17 @@ type NodeString string

// Draw satisfies the NodeValue interface.
func (s NodeString) Draw() *drawer.Drawer {
d, err := drawer.NewDrawer(len(s), 1)
d, err := drawer.NewDrawer(utf8.RuneCountInString(string(s)), 1)
if err != nil {
log.Fatal(fmt.Errorf("error while allocating new drawer in NodeString.Draw: %v", err))
}
for i, r := range s {
i := 0
for _, r := range s {
err := d.DrawRune(r, i, 0)
if err != nil {
log.Fatal(fmt.Errorf("error while drawing %d th rune of %s string in NodeString.Draw() method: %v", i, s, err))
}
i++
}
return d
}
2 changes: 1 addition & 1 deletion tree/tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func (t *Tree) Children() []*Tree {
return t.children
}

// Child returns the ith child of t.
// Child returns the i-th child of t.
func (t *Tree) Child(i int) (child *Tree, err error) {
if i < 0 || i >= len(t.children) {
return nil, fmt.Errorf("there is no child with index %d", i)
Expand Down

0 comments on commit 660a6f1

Please sign in to comment.