I'm working on cryptopals challenge set 1 challenge 3:
The hex encoded string:
1b37373331363f78151b7f2b783431333d78397828372d363c78373e783a393b3736
... has been XOR'd against a single character. Find the key, decrypt the message.
You can do this by hand. But don't: write code to do it for you.
How? Devise some method for "scoring" a piece of English plaintext. Character frequency is a good metric. Evaluate each output and choose the one with the best score.
I've programmed this:
extern crate num_traits;
use num_traits::pow;
use std::io;
fn main() {
let mut hex_string = String::new();
let mut bin_string = String::new();
let mut vec_bin: Vec<String> = Vec::new();
let mut vec_plain: Vec<String> = Vec::new();
println!("Please enter the string: ");
io::stdin()
.read_line(&mut hex_string)
.expect("Failed to read line");
bin_string = string_to_hex(hex_string.trim());
for i in 0..256 {
let mut tmp = bin_string.clone();
vec_bin.push(xor(tmp, i));
}
for s in vec_bin {
let mut vec_utf8: Vec<u8> = Vec::new();
for i in 0..(s.len() / 8) {
vec_utf8.push(bits_to_int(s[(i * 8)..((i * 8) + 8)].to_string()));
}
vec_plain.push(String::from_utf8(vec_utf8).unwrap());
}
for s in vec_plain {
println!("{}", s);
}
}
fn string_to_hex(text: &str) -> String {
let mut hex_string = String::new();
for c in text.chars() {
hex_string.push_str(&hex_to_bits(c));
}
hex_string
}
fn hex_to_bits(c: char) -> String {
let mut result = String::new();
let x = c.to_digit(16).unwrap();
let mut tmp = x;
while tmp != 0 {
result = (tmp % 2).to_string() + &result;
tmp = tmp / 2;
}
while result.len() < 4 {
result = "0".to_string() + &result;
}
result
}
fn xor(s: String, u: u16) -> String {
let mut result = String::new();
let bin = dec_to_bin(u);
for i in 0..(s.len() / 8) {
let mut tmp = bin.clone();
result = result + &single_byte_xor(s[(i * 8)..((i * 8) + 8)].to_string(), tmp);
}
result
}
fn dec_to_bin(u: u16) -> String {
let mut result = String::new();
let mut tmp = u;
while tmp != 0 {
result = (tmp % 2).to_string() + &result;
tmp = tmp / 2;
}
while result.len() < 8 {
result = "0".to_string() + &result;
}
result
}
fn single_byte_xor(s: String, u: String) -> String {
let mut result = String::new();
for i in 0..s.len() {
if &s[i..(i + 1)] == "1" && &u[i..(i + 1)] == "1" {
result = result + &"0".to_string();
} else if &s[i..(i + 1)] == "1" || &u[i..(i + 1)] == "1" {
result = result + &"1".to_string();
} else {
result = result + &"0".to_string();
}
}
result
}
fn bits_to_int(s: String) -> u8 {
let mut result: u8 = 0;
let mut counter = 0;
for c in s.chars().rev() {
let x = c.to_digit(10).unwrap();
if x == 1 {
result += pow(2u8, counter);
}
counter += 1;
}
result
}
When I run the program, I get this error:
Please enter the string:
1b37373331363f78151b7f2b783431333d78397828372d363c78373e783a393b3736
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: FromUtf8Error { bytes: [155, 183, 183, 179, 177, 182, 191, 248, 149, 155, 255, 171, 248, 180, 177, 179, 189, 248, 185, 248, 168, 183, 173, 182, 188, 248, 183, 190, 248, 186, 185, 187, 183, 182], error: Utf8Error { valid_up_to: 0, error_len: Some(1) } }', /checkout/src/libcore/result.rs:916:5
stack backtrace:
0: std::sys::unix::backtrace::tracing::imp::unwind_backtrace
at /checkout/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs:49
1: std::sys_common::backtrace::print
at /checkout/src/libstd/sys_common/backtrace.rs:68
at /checkout/src/libstd/sys_common/backtrace.rs:57
2: std::panicking::default_hook::{{closure}}
at /checkout/src/libstd/panicking.rs:381
3: std::panicking::default_hook
at /checkout/src/libstd/panicking.rs:397
4: std::panicking::rust_panic_with_hook
at /checkout/src/libstd/panicking.rs:577
5: std::panicking::begin_panic
at /checkout/src/libstd/panicking.rs:538
6: std::panicking::begin_panic_fmt
at /checkout/src/libstd/panicking.rs:522
7: rust_begin_unwind
at /checkout/src/libstd/panicking.rs:498
8: core::panicking::panic_fmt
at /checkout/src/libcore/panicking.rs:71
9: core::result::unwrap_failed
at /checkout/src/libcore/macros.rs:23
10: <core::result::Result<T, E>>::unwrap
at /checkout/src/libcore/result.rs:782
11: nr3::main
at src/main.rs:24
12: std::rt::lang_start::{{closure}}
at /checkout/src/libstd/rt.rs:74
13: std::panicking::try::do_call
at /checkout/src/libstd/rt.rs:59
at /checkout/src/libstd/panicking.rs:480
14: __rust_maybe_catch_panic
at /checkout/src/libpanic_unwind/lib.rs:101
15: std::rt::lang_start_internal
at /checkout/src/libstd/panicking.rs:459
at /checkout/src/libstd/panic.rs:365
at /checkout/src/libstd/rt.rs:58
16: std::rt::lang_start
at /checkout/src/libstd/rt.rs:74
17: main
18: __libc_start_main
19: _start
I can not figure out what the problem is.
You should be checking if the string you've got is a valid UTF-8 string. String::from_utf8 will check this for you and returns either Ok(s) or Err(_).
You should then match on the return value and either print the decoded string, or report a failure. Something like this does the trick.
use std::io;
fn main() {
let mut hex_string = "1b37373331363f78151b7f2b783431333d78397828372d363c78373e783a393b3736";
let raw:Vec<u8> = hex_string_to_raw(hex_string);
for i in 0..255 {
// Get XOR'd version
let mut vec_utf8:Vec<u8> = raw.iter().map(|v| v ^ i ).collect();
// Try to decode it
let s = String::from_utf8(vec_utf8);
match s {
Err(_) => { println!("Failed to decode using {}", i); }
Ok(s) => { println!("Decoded with {} to '{}'", i, s); }
}
}
}
fn hex_string_to_raw(hex: &str) -> Vec<u8> {
let chars:Vec<char> = hex.chars().collect();
let mut raw:Vec<u8> = vec![];
for cs in chars.chunks(2) {
let c0:char = cs[0];
let c1:char = cs[1];
raw.push( (c0.to_digit(16).unwrap()*16 + c1.to_digit(16).unwrap()) as u8);
}
raw
}
I solved it by changing this
for s in vec_bin {
let mut vec_utf8: Vec<u8> = Vec::new();
for i in 0..(s.len()/8) {
vec_utf8.push(bits_to_int(s[(i*8)..((i*8)+8)].to_string()));
}
vec_plain.push(String::from_utf8(vec_utf8).unwrap());
}
to this
for s in vec_bin {
let mut vec_utf8: Vec<u8> = Vec::new();
for i in 0..(s.len()/8) {
vec_utf8.push(bits_to_int(s[(i*8)..((i*8)+8)].to_string()));
}
let mut tmp = String::new();
tmp.push_str(&String::from_utf8_lossy(&vec_utf8));
vec_plain.push(tmp);
}
Related
My Near contact contract returns an error when I attempt to access a Persistent vector of object instances in the listcourses function.
Log [dev-1642726071766-23690329778292]: ABORT: Cannot parse JSON, filename: "~lib/assemblyscript-json/decoder.ts" line: 144 col: 5
Failure [dev-1642726071766-23690329778292]: Error: {"index":0,"kind":{"ExecutionError":"Smart contract panicked: Cannot parse JSON, filename: \"~lib/assemblyscript-json/decoder.ts\" line: 144 col: 5"}}
ServerTransactionError: {"index":0,"kind":{"ExecutionError":"Smart contract panicked: Cannot parse JSON, filename: \"~lib/assemblyscript-json/decoder.ts\" line: 144 col: 5"}}
at Object.parseResultError (/home/jjsullivan/.nvm/versions/node/v17.2.0/lib/node_modules/near-cli/node_modules/near-api-js/lib/utils/rpc_errors.js:31:29)
at Account.signAndSendTransactionV2 (/home/jjsullivan/.nvm/versions/node/v17.2.0/lib/node_modules/near-cli/node_modules/near-api-js/lib/account.js:160:36)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async scheduleFunctionCall (/home/jjsullivan/.nvm/versions/node/v17.2.0/lib/node_modules/near-cli/commands/call.js:57:38)
at async Object.handler (/home/jjsullivan/.nvm/versions/node/v17.2.0/lib/node_modules/near-cli/utils/exit-on-error.js:52:9) {
type: 'FunctionCallError',
import { storage, context, PersistentVector } from "near-sdk-as";
import { Course } from "./course";
// return the string 'hello world'
export function helloWorld(): string {
return 'hello world and \r\ngreetings earthlings from the Blockchain Academy'
}
export function welcomeUser(): string {
return "Greetings " + context.sender + " .... :)"
}
let studentlist = new PersistentVector<string>('student');
let absentlist = new PersistentVector<string>('absent');
let roster = new PersistentVector<string>('list');
let courselist = new PersistentVector<Course>('courses');
#mutateState()
export function initial(): string{
if(roster.length < 1 ){
//let courselist = new PersistentVector<Course>('courses');
let slist = new Array<string>();
let courseqty = new Array<Course>();
let scount: i32 = 0;
let strstuds: string = "";
let course0 = new Course(101, "Java", true);
let crs: string = "";
courselist.push(course0);
let courseamt: i32 = 0;
slist[0] = "Rob Sims";
scount++;
slist[1] = "Mike Dorgan";
scount++;
slist[2] = "Tom Reager";
scount++;
slist[3] = "Mary Smith";
scount++;
slist[4] = "Ryan Williams";
scount++;
slist[5] = "Bryant Nielson";
scount++;
slist[6] = "Ryan Kraz";
scount++;
for(let j = 0; j < scount; j++){
studentlist.push(slist[j]);
roster.push(slist[j]);
const mystudent = slist[j];
strstuds = strstuds + " " + j.toString() + ": " + mystudent + ", ";
}
courseamt = courselist.length;
strstuds = strstuds + " \nThere is " + courseamt.toString() + " course. The course is: " + course0.get_coursename();
return strstuds;
}else {
return "Students may be initialized.";
}
}
export function markabsent(studentnum: i32): string {
if(studentlist.containsIndex(studentnum)){
let abstudent: string = "";
abstudent = studentlist[studentnum];
studentlist.swap_remove(studentnum);
absentlist.push(abstudent);
return "Student " + abstudent + " marked absent.";
}
return "ok";
}
export function listabsent(): Array<string> {
let abstudlist = new Array<string>();
let indx = new Array<i32>();
let i: i32 = 0;
while (i < absentlist.length){
indx[i] = i;
abstudlist.push(indx[i].toString() + " : " + absentlist[i]);
i++;
}
return abstudlist;
}
export function addStudent(student: string): string{
let mystudent: string = "";
mystudent = student;
studentlist.push(mystudent);
roster.push(mystudent);
return "Student " + mystudent + " added. There are now " + studentlist.length.toString() + " students.";
}
export function removeStudent(rmstud: string): string{
let ablist = new Array<string>();
let studlist = new Array<string>();
let rmstudent: string = "";
let abstudent: string = "";
let stud: string = "";
let abindx = new Array<i32>();
let absentidx: i32;
//let ridx = new Array<i32>();
//let abidx = new Array<i32>();
let x: i32 = 0;
let y: i32 = 0;
while (y < studentlist.length){
//ridx[y] = y;
stud = studentlist[y];
if(stud == rmstud){
rmstudent = studentlist.swap_remove(y);
}
y++;
}
while (x < absentlist.length){
abstudent = absentlist[x];
if(abstudent == rmstud){
rmstudent = absentlist.swap_remove(x);
}
x++;
}
return "Removed " + rmstudent;
}
export function listStudents(): Array<string>{
let studlist = new Array<string>();
let indx = new Array<i32>();
let i: i32 = 0;
//
while (i < studentlist.length){
indx[i] = i;
studlist.push(indx[i].toString() + " : " + studentlist[i]);
i++;
}
return studlist;
}
export function movePresent(abstudnum: i32): string {
if(absentlist.containsIndex(abstudnum)){
let abstudent: string = "";
abstudent = absentlist[abstudnum];
absentlist.swap_remove(abstudnum);
studentlist.push(abstudent);
return "Student " + abstudent + " moved from absent tp present.";
}else {
return "Cannot find student with index " + abstudnum.toString() + ".";
}
}
export function addCourse(id: i32, cnm: string, offered: boolean): string{
let id0: i32 = 0;
let cnm0: string = "";
let offered0: boolean;
let clist: string = "";
id0 = id;
cnm0 = cnm;
offered0 = offered;
let len: i32 = 0;
len = courselist.length;
let newcourse = new Course(id0, cnm0, offered0);
courselist.push(newcourse);
len = courselist.length;
let addedcourse: string = "";
let courseid: i32 = 0;
let mycrs_name: string = "";
let noffered: boolean;
courseid = newcourse.get_coursenum();
mycrs_name = newcourse.get_coursename();
noffered = newcourse.get_offered();
//const thecourse = courselist[id];
addedcourse = "Added course. ID: " + courseid.toString() + " Name: " + newcourse.get_coursename() + " Offered: " + noffered.toString();
addedcourse = addedcourse + ". The course list length: " + len.toString();
return addedcourse;
}
#mutateState()
export function listcourses(): string{
let clist = new Array<Course>();
let z: i32 = 0;
let coursedata: string = "";
let myid: i32 = 0;
let mycname: string = "";
let myoffered: boolean;
//let courseinfo: Course;
while (z < courselist.length){
clist.push(courselist[z]);
coursedata = coursedata + " " + myid.toString() + " "+ mycname;
z++;
}
return coursedata + " " + z.toString();
}
Class code
//#nearBindgen
export class Course {
private coursenum: i32
private cname: string;
private offered: boolean;
constructor (coursenum: i32, cname: string, offered: boolean) {
this.cname = cname;
this.coursenum = coursenum;
this.offered = offered;
}
set_coursenum(): void {
this.coursenum = coursenum;
}
set_coursename(): void {
this.cname = cname;
}
set_offered(): void {
this.offered = true;
}
set_notoffered(): void {
this.offered = false;
}
get_coursenum(): i32 {
return this.coursenum;
}
get_coursename(): string {
return this.cname;
}
get_offered(): boolean {
return this.offered;
}
}
You have to enable the #nearBindgen annotation in your class.
It looks like it's commented out in the top of your class code. Remove the first two slashes "//" before // #nearBindgen.
That way, you're able to serialize and deserialize the class to and from JSON
#nearBindgen // remove the "//"
export class Course {
...
}
I'm trying to set the value 1 at each position of a two-dimensional vector (graph) using the crossbeam_utils version 0.8.0. The sub-matrices each thread receives are disjoint. I'm running the code with cargo run on Windows. My code is based on these question1 and question2. But, I'm getting the following error:
thread '<unnamed>' panicked at 'index out of bounds: the len is 3 but the index is 3'
However, if I comment the command (*slice)[row][col] = 1 in the threads spawning, I can see the threads working perfectly as printed in the console.
What can is it causing this error?
use crossbeam_utils::thread;
use std::slice;
const NTHREADS: usize = 2;
fn build_graph() -> Vec<Vec<usize>> {
let dictionary: Vec<String> = vec!["monk".to_string(), "mock".to_string(), "pock".to_string(),
"pork".to_string(), "perk".to_string(), "perl".to_string()];
let s_graph = dictionary.len();
let mut graph: Vec<Vec<usize>> = vec![vec![0; s_graph]; s_graph];
let chunk = f32::ceil(s_graph as f32 / NTHREADS as f32) as usize;
let mut sliced_graph: Vec<&mut [Vec<usize>]> = Vec::with_capacity(NTHREADS);
let ptr_graph = graph.as_mut_ptr();
let mut offset;
let mut count_of_items;
// Creating mutable references for slices of the matrix
for n_th in 0..NTHREADS {
offset = n_th * chunk;
count_of_items = if offset + chunk > s_graph { s_graph - offset } else { chunk };
unsafe {
sliced_graph.push(slice::from_raw_parts_mut(ptr_graph.offset((offset) as isize), count_of_items));
}
}
thread::scope(|scope| {
let mut n_th: usize = 0;
let mut min_bound;
let mut max_bound;
for slice in &mut sliced_graph {
min_bound = n_th * chunk;
max_bound = if min_bound + chunk > s_graph { s_graph } else { min_bound + chunk};
scope.spawn(move |_| {
println!("thread number: {} - ini: {} - end: {}", n_th, min_bound, max_bound);
for row in min_bound..max_bound {
for col in 0..s_graph {
println!("th:{} -->> set row:{} col:{}", n_th, row, col);
(*slice)[row][col] = 1;
}
}
println!("thread number: {} - Finished!", n_th);
});
n_th += 1;
}
}).unwrap();
graph
}
fn main() {
build_graph();
}
The solution proposed by #Masklinn:
use crossbeam_utils::thread;
use std::slice;
const NTHREADS: usize = 2;
fn build_graph() -> Vec<Vec<usize>> {
let dictionary: Vec<String> = vec!["monk".to_string(), "mock".to_string(), "pock".to_string(),
"pork".to_string(), "perk".to_string(), "perl".to_string()];
let s_graph = dictionary.len();
let mut graph: Vec<Vec<usize>> = vec![vec![0; s_graph]; s_graph];
let chunk = f32::ceil(s_graph as f32 / NTHREADS as f32) as usize;
let mut sliced_graph: Vec<&mut [Vec<usize>]> = Vec::with_capacity(NTHREADS);
let ptr_graph = graph.as_mut_ptr();
// Creating mutable references for slices of the matrix
for n_th in 0..NTHREADS {
let offset = n_th * chunk;
let count_of_items = if offset + chunk > s_graph { s_graph - offset } else { chunk };
unsafe {
sliced_graph.push(slice::from_raw_parts_mut(ptr_graph.offset((offset) as isize), count_of_items));
}
}
thread::scope(|scope| {
for (n_th, slice) in sliced_graph.iter_mut().enumerate() {
scope.spawn(move |_| {
println!("thread number: {} - {:?}", n_th, slice);
for (row, r) in slice.into_iter().enumerate() {
for (col, cell) in r.into_iter().enumerate() {
println!("th:{} -->> set row:{} col:{}", n_th, row, col);
*cell = 1;
}
}
println!("thread number: {} - Finished!", n_th);
});
}
}).unwrap();
graph
}
fn main() {
build_graph();
}
I want to create a signature for the myJD REST-API. But the first snippet is not working. I talked to the support and they told me that I need the unhexed key to create the correct hash. So I created a Byte-Array of the Hex-Value and changed the code to work again (see second snippet). Both methods have the same result.
Link to API documentation: https://docs.google.com/document/d/1IGeAwg8bQyaCTeTl_WyjLyBPh4NBOayO0_MAmvP5Mu4/edit?pref=2&pli=1
First:
import Foundation
extension String{
func hmacSHA256(key: String) -> String {
let str = self.cStringUsingEncoding(NSUTF8StringEncoding)
let strLen = Int(self.lengthOfBytesUsingEncoding(NSUTF8StringEncoding))
let digestLen = Int(CC_SHA256_DIGEST_LENGTH)
let result = UnsafeMutablePointer<CUnsignedChar>.alloc(digestLen)
let keyStr = key.cStringUsingEncoding(NSUTF8StringEncoding)!
let keyLen = Int(key.lengthOfBytesUsingEncoding(NSUTF8StringEncoding))
CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), keyStr, keyLen, str!, strLen, result)
let digest = stringFromResult(result, length: digestLen)
result.dealloc(digestLen)
return digest
}
private func stringFromResult(result: UnsafeMutablePointer<CUnsignedChar>, length: Int) -> String {
let hash = NSMutableString()
for i in 0..<length {
hash.appendFormat("%02x", result[i])
}
return String(hash)
}
}
Second:
func hmacSHA256_2(key: String) -> String {
//let cKey = key.dataUsingEncoding(NSUTF8StringEncoding)!
let bKey = Array(key.utf8)
let bData = Array(self.utf8)
//let cData = self.dataUsingEncoding(NSUTF8StringEncoding)!
var cHMAC = [CUnsignedChar](count: Int(CC_SHA256_DIGEST_LENGTH), repeatedValue: 0)
CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), bKey, Int(bKey.count), bData, Int(bData.count), &cHMAC)
let output = NSMutableString(capacity: Int(CC_SHA256_DIGEST_LENGTH))
for byte in cHMAC {
output.appendFormat("%02hhx", byte)
}
return output as String
}
I have a drawing app which is currently made up of a main View Controller which holds 4 separate UIViews which simultaneously replicate the line drawn on the touched quadrant across the other 3 with some axis reversed to make the drawing symmetrical.
When using this method the drawing is smooth and you can see that there are lots of points being collected when the user moves their finger as the line follows their movements quite well.
The code at a high level looks like this:
MainViewController.swift
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
var touch: UITouch = touches.anyObject() as UITouch
var p = CGPoint()
if touch.view == quadrant1 {
p = touch.locationInView(quadrant1)
quadrant1.began(p)
var p2 = CGPointMake(quadrant2.bounds.width - p.x, p.y)
quadrant2.began(p2)
var p3 = CGPointMake(p.x,quadrant3.bounds.height - p.y)
quadrant3.began(p3)
var p4 = CGPointMake(quadrant4.bounds.width - p.x, quadrant4.bounds.height - p.y)
quadrant4.began(p4)
} else if touch.view == quadrant2 {
...
Touches 'moved' and 'ended' call similar methods in each of the quadrants by doing the same calculations. The Quadrant files look like this:
Quadrant1,2,3,4.swift
// A counter to determine if there are enough points to make a quadcurve
var ctr = 0
// The path to stroke
var path = UIBezierPath()
// After the user lifts their finger and the line has been finished the same line is rendered to an image and the UIBezierPath is cleared to prevent performance degradation when lots of lines are on screen
var incrementalImage = UIImage()
// This array stores the points that make each line
var pts: [CGPoint] = []
override func drawRect(rect: CGRect) {
incrementalImage.drawInRect(rect)
path.stroke()
}
func began (beganPoint: CGPoint) {
ctr = 0
var p = beganPoint
pts.insert(beganPoint, atIndex: 0)
}
func moved(movedPoints: CGPoint) {
var p = movedPoints
ctr++
pts.insert(movedPoints, atIndex: ctr)
// This IF statement handles the quadcurve calculations
if ctr == 3 {
pts[2] = CGPointMake((pts[1].x + pts[3].x)/2.0, (pts[1].y + pts[3].y)/2.0);
path.moveToPoint(pts[0])
path.addQuadCurveToPoint(pts[2], controlPoint: pts[1])
self.setNeedsDisplay()
pts[0] = pts[2]
pts[1] = pts[3]
ctr = 1
}
}
func ended (endPoint: CGPoint) {
if ctr == 2 {
path.moveToPoint(pts[0])
path.addQuadCurveToPoint(pts[2], controlPoint: pts[1])
}
self.drawBitmap()
self.setNeedsDisplay()
path.removeAllPoints()
}
func drawBitmap() {
UIGraphicsBeginImageContextWithOptions(self.bounds.size, false, 0.0)
var rectPath = UIBezierPath(rect: self.bounds)
UIColor.clearColor().setFill()
rectPath.fill()
incrementalImage.drawAtPoint(CGPointZero)
color.setStroke()
path.stroke()
incrementalImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
}
So the above approach actually worked very well and produce fairly smooth lines like so but the user was always locked into using 4 quadrants because they were separate UIView's:
After some thinking we decided to scrap the 4 separate UIView's and use a single view to handle the drawing which would allow an arbitrary number of lines to be drawn at a time giving the user more options (8 lines for example), and this is where things got tricky.
The MainViewController no longer handles the touches interaction methods, the new 'DrawingView' captures the gestures itself with a UILongPressGestureRecogniser.
func handleLongPressDrawing(sender: UILongPressGestureRecognizer) {
var p = sender.locationInView(self)
switch sender.state {
case UIGestureRecognizerState.Began:
self.began(p)
break;
case UIGestureRecognizerState.Changed:
self.moved(p)
break;
case UIGestureRecognizerState.Ended:
self.ended(p)
default:
break;
}
}
The methods now reference a new DrawingElement class to perform the symmetry calculations:
enum GridType {
case ONE, TWO_1, TWO_2, TWO_3, TWO_4, THREE, FOUR_1, FOUR_2, FIVE, SIX_1, SIX_2, SEVEN, EIGHT_1, SIXTEEN
}
enum DrawingElementType {
case PATH, POINT, CIRCLE
}
class DrawingElement: NSObject {
var points : [CGPoint] = []
private var drawingWidth : CGFloat!
private var drawingHeight : CGFloat!
private var gridType : GridType!
private var drawingElementType : DrawingElementType!
init(gridType : GridType, drawingWidth : CGFloat, drawingHeight : CGFloat) {
self.gridType = gridType
self.drawingWidth = drawingWidth
self.drawingHeight = drawingHeight
super.init()
}
func getPoints() -> [CGPoint] {
return points
}
func addPoint(pointCG: CGPoint) {
points.append(pointCG)
}
func getPoint(pos : Int) -> CGPoint {
return points[pos]
}
func getDrawingWidth() -> CGFloat {
return drawingWidth
}
func setDrawingWidth(w : CGFloat) {
drawingWidth = w
}
func getDrawingWidthCG() -> CGFloat {
return CGFloat(drawingWidth)
}
func getDrawingHeight() -> CGFloat {
return drawingHeight
}
func setDrawingHeight(h : CGFloat) {
drawingHeight = h
}
func getDrawingHeightCG() -> CGFloat {
return CGFloat(drawingHeight)
}
func getPointCount() -> Int {
return points.count
}
func getDrawingElementType() -> DrawingElementType {
return drawingElementType
}
func setDrawingElementType(det : DrawingElementType) {
drawingElementType = det
}
func getGridType() -> GridType {
return gridType
}
func setGridType(gt : GridType) {
gridType = gt
}
func smoothLinesPart1() {
points[2] = CGPointMake((points[1].x + points[3].x)/2.0, (points[1].y + points[3].y)/2.0)
}
func smoothLinesMoveTo() -> CGPoint {
return points[0]
}
func smoothLinesQuadCurve() -> (CGPoint, CGPoint) {
return (points[2], points[1])
}
func smoothLinesReorderArray() {
points[0] = points[2]
points[1] = points[3]
}
func getCalculatedPoints(allPoints : [CGPoint]) -> [Int : [CGPoint]] {
var newPoints = [CGPoint]()
var numberOfPoints : Int!
var temp : CGFloat!
var x : CGFloat!
var y : CGFloat!
//println("Before Path points: \(allPoints)")
var pathPoints = [Int() : [CGPoint]()]
if(gridType == GridType.EIGHT_1) {
numberOfPoints = 8
} else if(gridType == GridType.ONE) {
numberOfPoints = 1
} else if(gridType == GridType.TWO_1) {
numberOfPoints = 2
} else if(gridType == GridType.FOUR_1) {
numberOfPoints = 4
}
var firstTime = true
for point in allPoints {
x = point.x
y = point.y
if(gridType == GridType.EIGHT_1 || gridType == GridType.ONE || gridType == GridType.TWO_1 || gridType == GridType.FOUR_1) {
if(firstTime) {
for i in 1...numberOfPoints {
switch (i) {
case 5:
temp = y;
y = x;
x = temp;
pathPoints[4] = [CGPoint(x: x, y: y)]
case 1:
pathPoints[0] = [CGPoint(x: x, y: y)]
//println(" first point\(pathPoints[0])")
break;
case 2:
pathPoints[1] = [CGPoint(x: (x - getDrawingWidthCG()) * -1, y: y)]
break;
case 6:
pathPoints[5] = [CGPoint(x: (x - getDrawingWidthCG()) * -1, y: y)]
break;
case 3:
pathPoints[2] = [CGPoint(x: x, y: (y - getDrawingHeightCG()) * -1)]
break;
case 7:
pathPoints[6] = [CGPoint(x: x, y: (y - getDrawingHeightCG()) * -1)]
break;
case 4:
pathPoints[3] = [CGPoint(x: (x - getDrawingWidthCG()) * -1, y: (y - getDrawingHeightCG()) * -1)]
break;
case 8:
pathPoints[7] = [CGPoint(x: (x - getDrawingWidthCG()) * -1, y: (y - getDrawingHeightCG()) * -1)]
break;
default:
break
//newPoints.append(CGPoint(x: x, y: y))
}
}
firstTime = false
} else {
for i in 1...numberOfPoints {
switch (i) {
case 5:
temp = y;
y = x;
x = temp;
pathPoints[4]?.append(CGPoint(x: x, y: y))
case 1:
pathPoints[0]?.append(CGPoint(x: x, y: y))
//println(" first point\(pathPoints[0])")
break;
case 2:
pathPoints[1]?.append(CGPoint(x: (x - getDrawingWidthCG()) * -1, y: y))
break;
case 6:
pathPoints[5]?.append(CGPoint(x: (x - getDrawingWidthCG()) * -1, y: y))
break;
case 3:
pathPoints[2]?.append(CGPoint(x: x, y: (y - getDrawingHeightCG()) * -1))
break;
case 7:
pathPoints[6]?.append(CGPoint(x: x, y: (y - getDrawingHeightCG()) * -1))
break;
case 4:
pathPoints[3]?.append(CGPoint(x: (x - getDrawingWidthCG()) * -1, y: (y - getDrawingHeightCG()) * -1))
break;
case 8:
pathPoints[7]?.append(CGPoint(x: (x - getDrawingWidthCG()) * -1, y: (y - getDrawingHeightCG()) * -1))
break;
default:
break
//newPoints.append(CGPoint(x: x, y: y))
}
}
}
}
}
}
And this is called at various parts of the DrawingViews interaction handlers:
var paths = [Int() : UIBezierPath()]
func began (beganPoint: CGPoint) {
strokes = 0
var p = beganPoint
ctr = 0
//pts.insert(beganPoint, atIndex: 0)
drawingElement?.addPoint(beganPoint)
}
func moved(movedPoints: CGPoint) {
strokes++
var p = movedPoints
ctr++
drawingElement?.addPoint(movedPoints)
if ctr == 3 {
drawingElement?.smoothLinesPart1()
path.moveToPoint(drawingElement!.smoothLinesMoveTo())
path.addQuadCurveToPoint(drawingElement!.smoothLinesQuadCurve().0, controlPoint: drawingElement!.smoothLinesQuadCurve().1)
self.setNeedsDisplay()
drawingElement?.smoothLinesReorderArray()
ctr = 1
}
var pointsArray : [CGPoint] = drawingElement!.getPoints()
var calcArray = drawingElement?.getCalculatedPoints(pointsArray)
let sortedCalcArray = sorted(calcArray!) { $0.0 < $1.0 }
if pointsArray.count > 1 {
for (pIndex, path) in sortedCalcArray {
paths[pIndex] = UIBezierPath()
for var i = 0; i < path.count; i++ {
paths[pIndex]!.moveToPoint(path[i])
if(i > 0) {
paths[pIndex]!.addLineToPoint(path[i-1])
}
self.setNeedsDisplay()
}
}
}
override func drawRect(rect: CGRect) {
for (index, path) in paths {
path.lineCapStyle = kCGLineCapRound
path.lineWidth = lineWidth
color.setStroke()
path.stroke()
}
color.setStroke()
incrementalImage.drawInRect(rect)
}
}
I have a feeling that either 1) The iPhone does like drawing 4 or more paths within a single view at a time, or 2) the performance is degraded because of the number of loops that are running each time the user moves their finger. Here is what a similar line looks like with the above new code:
So after all of that I am wondering if anyone would be able to shed some light on why the new code draws so differently or what a better approach may be.
Thanks
So after some trial and error I kept most of the code above, the only notable difference was that I constructed 4 separate UIBezierPaths and eliminated the for loop nested in the other for loop. That seemed to be causing the issue
Hi I have written a class in swift that should create a maze through a recursive backtracking algorithm. I seem to have a problem around the assignment of walls to my maze. But I can not crack it.
It would be great if I could get some help here, thank you!
Please find a description of the code below
2D Array Class - Pretty self explanatory. Give it a number of columns and rows and a default value, from there it generate the 2d array. The subscript methods allow you to set and get values.
class Array2D<T> {
let columns: Int
let rows: Int
var array : Array<Array<T?>>
init(columns: Int, rows: Int, repeatedValue: T?) {
self.columns = columns
self.rows = rows
var tmp = Array<T?>(count: rows, repeatedValue: repeatedValue)
array = Array<Array<T?>>(count: columns, repeatedValue: tmp)
}
subscript(column: Int, row: Int) -> T? {
get {
return array[column][row]
}
set(newValue) {
array[column][row] = newValue
}
}
}
DIR enum A enum which allows us to abstract ourselves from bits by assigning them names.
enum DIR : UInt8 {
case N = 1
case S = 2
case E = 4
case W = 8
case O = 0
}
Direction Class This class holds all the information about the Directions. It may be slightly an overkill. It allows you to get directions (N,S,E,W) in relation to a current location. You can also get the opposite direction.
class Direction {
var bit : DIR
var dx : Int
var dy : Int
init(bit: DIR, dx: Int, dy: Int) {
self.bit = bit
self.dx = dx
self.dy = dy
}
class func NORTH() -> Direction {
return Direction(bit: DIR.N, dx: 0, dy: -1)
}
class func SOUTH() -> Direction {
return Direction(bit: DIR.S, dx: 0, dy: 1)
}
class func EAST() -> Direction {
return Direction(bit: DIR.E, dx: 1, dy: 0)
}
class func WEST() -> Direction {
return Direction(bit: DIR.W, dx: -1, dy: 0)
}
func opposite() -> Direction {
switch(bit){
case DIR.N:
return Direction.SOUTH()
case DIR.S:
return Direction.NORTH()
case DIR.E:
return Direction.WEST()
case DIR.W:
return Direction.EAST()
default:
println("An error occured while returning the opposite of the direction with bit: \(bit)")
return Direction(bit: DIR.O, dx: 0, dy: 0)
}
}
}
RecursiveBackTracking Class This is where the magic happens. This class auto generates a maze given a width(x) and a height(y). The generateMaze() function does most of the work with the other functions in support. Everything here seems to work but I am still not getting the appropriate result. Potentially the problem may also by in the display() function.
class RecursiveBacktracking {
var x : Int
var y : Int
var maze : Array2D<UInt8>
init(x: Int, y: Int) {
self.x = x
self.y = y
maze = Array2D<UInt8>(columns: x, rows: y, repeatedValue: 0)
generateMaze(0, cy: 0)
display()
}
func generateMaze(cx: Int, cy: Int) {
var directions : [Direction] = [Direction.NORTH(),Direction.SOUTH(),Direction.EAST(),Direction.WEST()]
directions = shuffle(directions)
for dir in directions {
var nx : Int = cx + dir.dx
var ny : Int = cx + dir.dy
if between(nx, upper: x) && between(ny, upper: y) && getMazeObject(nx, y: ny) == 0 {
maze[cx,cy] = bitwiseOr(getMazeObject(cx, y: cy), b: dir.bit.rawValue)
maze[nx,ny] = bitwiseOr(getMazeObject(nx, y: ny), b: dir.opposite().bit.rawValue)
generateMaze(nx, cy: ny)
}
}
}
func bitwiseOr(a: UInt8, b: UInt8) -> UInt8 {
return a | b
}
func getMazeObject(x: Int, y: Int) -> UInt8 {
if var object = maze[x,y] {
return object
}else{
println("No object could be found at location: (\(x),\(y)).")
return 0
}
}
func between(v: Int, upper: Int) -> Bool {
return (v>=0) && (v<upper)
}
func shuffle<C: MutableCollectionType where C.Index == Int>(var list: C) -> C {
let count : Int = Int(countElements(list))
for i in 0..<(count - 1) {
let j = Int(arc4random_uniform(UInt32(count - i))) + i
swap(&list[i], &list[j])
}
return list
}
func display() {
for i in 0..<y {
// Draw North Edge
for j in 0..<x {
var bit : UInt8 = getMazeObject(j, y: i)
bit = bit & 1
if bit == 0 {
print("+---")
}else{
print("+ ")
}
}
println("+")
// Draw West Edge
for j in 0..<x {
var bit : UInt8 = getMazeObject(j, y: i)
bit = bit & 8
if bit == 0 {
print("| ")
}else{
print(" ")
}
}
println("|")
}
// Draw the bottom line
for j in 0..<x {
print("+---")
}
println("+")
}
}
Additional Information: This algorithm is based off of http://rosettacode.org/wiki/Maze#Java
The error is here:
var nx : Int = cx + dir.dx
var ny : Int = cx + dir.dy
The second cx should be cy.
Remark: There is some room for improvement in your code. As an example, the
bitwise or | is already defined for UInt8, so there is no need to define that
as a function. If you have fixed your code to work correctly, you might consider
to post it at http://codereview.stackexchange.com to get a review.