Developer42

2013-07-28

Countdown Numbers Round Solver in T-SQL

Filed under: Microsoft, SQL Server, Technology — Tags: , , , , , , , — Developer42 @ 14:00

Every now and then I try to create programs to solve puzzles as a way to introduce myself to new ideas & test my coding skills. My latest self-challenge was to write SQL code to solve the Countdown Numbers Round. The idea is; given 6 random numbers (each of which may be used at most once) and using only basic arithmetic (i.e. addition, subtraction, multiplication and division) to get to a randomly generated target number. e.g. Given the numbers 3, 7, 9, 10, 50 & 75, and the target 396 one solution would be: 3 * (75 + 50 + 7).

Here’s how I approached it in T-SQL:
Run this solution at SQL Fiddle

create procedure dbo.CountdownNumberSolver
(
	@target int output --output used to allow these to be referenced outside of the proc if rnd numbers are generated
	, @number1 int output
	, @number2 int output
	, @number3 int output
	, @number4 int output
	, @number5 int output
	, @number6 int output 
) as
begin
	
	--if the user didn't specify values randomly pick some.
	--   Small Number (1-10):			1 + 10 * rand() 
	--   Large Number (25,50,75,100):	floor(1 + 4 * rand()) * 25
	--   Target (1-999):				1 + (999 * rand())
	select @target = ISNULL(@target, 1 + (999 * rand())) --I assume 0 isn't a valid solution in countdown?
		, @number1 = ISNULL(@number1,1 + 10 * rand())
		, @number2 = ISNULL(@number2,1 + 10 * rand())
		, @number3 = ISNULL(@number3,1 + 10 * rand())
		, @number4 = ISNULL(@number4,1 + 10 * rand())
		, @number5 = ISNULL(@number5,floor(1 + 4 * rand()) * 25)
		, @number6 = ISNULL(@number6,floor(1 + 4 * rand()) * 25)

	--output question
    /* commented out as sql fiddle only returns first result set
	select @target [target]
		, @number1 [#1]
		, @number2 [#2]
		, @number3 [#3]
		, @number4 [#4]
		, @number5 [#5]
		, @number6 [#6]
	*/
	--records combinations tried so far / partial equations
	create table #solutions
	(
		id bigint not null identity(1,1) primary key clustered
		, binaryFlags tinyint not null
		, number float not null
		, equation nvarchar(256) not null
	)
	create index ix_solutions_number on #solutions(number)
	
	--start with the given values - the id stuff is just to make it easy to reuse this procedure should we want a different # of source numbers
	insert #solutions
	select power(2,id) 
	, number
	, CAST(number as nvarchar(3))
	from 
	(values 
		 (0,@number1)
		,(1,@number2)
		,(2,@number3)
		,(3,@number4)
		,(4,@number5)
		,(5,@number6)
	)x(id, number)

	declare @previousCount bigint = 0
	, @newCount bigint = (select COUNT(1) from #solutions)
	, @tempCount bigint
	
	while @previousCount < @newCount --repeat whilst there are still untried combos
		and not exists (select top 1 1 from #solutions where number=@target) --and we're still looking for a solution
	begin

		set @tempCount = @newCount
		
		insert #solutions
		select a.binaryFlags | b.binaryFlags
		, case x.o
			when '+' then a.number + b.number
			when '-' then a.number - b.number
			when '*' then a.number * b.number
			when '/' then a.number / b.number
		end
		, '(' + a.equation + ' ' + x.o + ' ' + b.equation + ')'
		from #solutions a
		inner join #solutions b
			on a.binaryFlags & b.binaryFlags = 0 --ensure we're not reusing source numbers
			and a.id != b.id --minor (potential) optimisation
			and --no point comparing things we've already checked (this may allow for some performance improvement- it doesn't affect the logic)
			(
				   a.id > @previousCount
				or b.id > @previousCount
			)
		inner join --doing things this way means we only need to find the new combinations from a&b once, not 4 times
		(
			values ('+'), ('-'), ('*'), ('/')
		) x(o)
			on  not(x.o = '/' and b.number = 0) --avoid div 0 errors
			and not(x.o = '-' and a.number - b.number = 0) --don't create 0 value entries (if 0's not an allowed solution this result just adds overhead without value)
			and not(x.o = '+' and a.number + b.number = 0) --don't create 0 value entries (if 0's not an allowed solution this result just adds overhead without value)
			and not(x.o = '+' and a.id > b.id) --commutative function optimisation
			and not(x.o = '*' and a.id > b.id) --commutative function optimisation
		
		set @previousCount = @tempCount
		select @newCount = COUNT(1) from #solutions	
		
	end

	--limited to one answer to fit with the game / avoid the assumption that all possible solutions would be listed 
	--(since we exit the above loop the moment we have an answer rather than continuing to check all combos)
	select top 1 
	  @number1 [#1]
	, @number2 [#2]
	, @number3 [#3]
	, @number4 [#4]
	, @number5 [#5]
	, @number6 [#6]
	, @target  [target] 
	, equation [solution]
	from #solutions 
	where number = @target
	order by len(equation) --show the shortest equation as that's probably simplest
	option (fast 1)	

	if object_id('tempdb..#solutions','U') is not null drop table #solutions

	return 0
end

Here’s an example of how to run it:

--run the proc using random numbers (the proc replaces nulls with random #s)
exec dbo.CountdownNumberSolver null, null, null, null, null, null, null

--run the proc on user defined values
exec dbo.CountdownNumberSolver 396, 7, 3, 10, 9, 50, 75

2013-06-05

Reverse an Array in a Windows Batch File

Filed under: Technology — Tags: , , , , , — Developer42 @ 17:58

The below script demonstrates how to reverse the order of elements in an array in a Windows batch file.

@echo off
cls
echo Script Started
setlocal enableextensions
setlocal enabledelayedexpansion

set arrayTest=("1", "2", "3", "test")

echo.Forward: %arrayTest%
for %%1 in %arrayTest% do (call:outputElement %%1)

call:reverseArray %arrayTest%

echo.Backward: %reverseArray%
for %%1 in %reverseArray% do (call:outputElement %%1)

goto:end

:reverseArray
set reverseArray=
for %%1 in %* do (call:reverseArrayX %%1)
set reverseArray=(%reverseArray%)
@goto:eof

:reverseArrayX
if "%reverseArray%"=="" (set reverseArray=%1) else (set reverseArray=%1, %reverseArray%)
@goto:eof

:outputElement
echo. – %~1
@goto:eof

:end
Echo.Completed

2013-05-02

How’re you doing? The continuous employee engagement survey.

Background
For the last few years my company’s been doing annual employee engagement surveys; a questionnaire asking how much you enjoy working at the place, what the best and worst things are, whether you’re thinking of leaving, etc. All of this data is aggregated and anonymised then a few months later presented back to us where we’re asked to help guide the company forwards to get the scores up the following year (and hopefully make the company a better place to work in the process).

Problem
The issue with this is you’re collecting data one day a year.

  • It’s pretty hard to remember what’s happened in that year, not comment on things from previous years, determine what’s still relevant to report on and mostly avoid biasing your results based on your current mood / the week’s events. As a result this kind of information isn’t very revealing.
  • As things are applied to try to resolve any issues and improve work life in general there’s no official feedback mechanism until the following year; at which point it’s hard to rate what worked and what didn’t.

Proposed Solution
A button. At the end of each day before going home employees go to a page on the intranet and press a button ["Woo" | "Meh" | "Ugh"] (number of options and their descriptions can vary per implementor’s preference). They may optionally also add a short comment (though should use this feature sparingly). This is then registered along with the date and their username (anonymised if required; but in such a way that the user cannot submit multiple answers per day) and fed into a database. Next you have a graph showing an aggregated view of all employees satisfaction ratings (depending on anonymity preferences you can do this at individual levels, for teams, for offices, or for the whole company). You can now compare these results with any activities taking place to see what’s upsetting people and what’s keeping them motivated. You can also see problem areas and upcoming issues (if there’s a long period of negative scores or everyone on a team gives a negative at the same time) and thus investigate and resolve them before they escalate. The comment function may provide additional information on why the scores are good/bad, and will also be useful if you continue with the annual detailed questionnaire as employees can look at their mood changes and the few comments they made throughout the year to remember key events to feed into this detail. Other benefits can come from including other info into your analytics (project deadlines, holidays, sick days, socials, weather (you can’t control it but can see its effect and compensate), etc).
Since it’s just a button it’s not a burden – so people will be likely to use it. You also get the satisfaction of stamping a close to your work day.

Questions

  • What do you guys think?
  • Has anyone done something like this at their place?
  • Anyone know of a LifeHacker style site for doing this kind of thing already which could be utilised (it’s pretty easy to build, but if there’s stuff out there which already includes common data sets (e.g. weather in your area, ability to log meals to allow individuals to make themselves happier by eating healthier, etc. so much the better)?

To keep discussion in one place I’ve disabled comments on this post and have left a discussion on Hacker News: https://news.ycombinator.com/item?id=5646466.

2013-02-13

Harlem Shake (XKCD Edition)

Filed under: Memes — Tags: , , , , , , , , , — Developer42 @ 23:38

I recently came across two new things. The first, an excellent bit of code by Antonin Hildebrand cmx.io. This code allows people to easily create XKCD style comics through simple markup, and also includes a useful wysiwyg editor to make tweaking the pictures simpler.
The second was a slew of videos in my YouTube feed with the latest meme: The Harlem Shake.
My instant reaction was to create my first meme whilst trying out my new toy: here’s the result (click the image to go to the animated markup version):

XKCD Harlem Shake http://cmx.io/#4949114

Harlem Shake XKCD Edition created with cmx.io

Code included below (also available on GitHub: https://gist.github.com/JohnLBevan/4949114.

Update (2013-02-14 22:12 UTC)
Code updated to include animation of second frame.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="http://cmx.io/v/0.1/cmx.css"/>
<script src="http://cmx.io/v/0.1/cmx.js"></script>
<script type="text/javascript">
<!--
  var frame2 = true;
  var vis = {};
  vis[true] = "block";
  vis[false] = "none";
  window.onload = function(){document.getElementById("scene2y").style.display = vis[false];};
  setTimeout(function(){setInterval(function(){swapFrames()},200);},3000);
  function swapFrames()
  {
    document.getElementById("scene2x").style.display = vis[frame2];
    frame2 = !frame2;
    document.getElementById("scene2y").style.display = vis[frame2];
  }
-->  
</script>
<style>.cmx-user-scene4 .cmx-text-border .cmx-path {stroke: orange}</style>
</head>
<body>
  <div style="max-width:900px; -webkit-transform:rotate(0deg);">

    <scene id="scene1">
      <label t="translate(0,346)">
        <tspan x="0" y="0em">Harlem Shake (XKCD edition)</tspan>
      </label>
      <drawing t="translate(0,31)">
        <line stroke="green">
          <point x="0" y="0"></point>
          <point x="250" y="20"></point>
        </line>
      </drawing>
      <!-- helmet; place behind head to cheat the circle -->
      <drawing t="translate(55,110)">
        <line stroke="black">
          <point x="0" y="0"></point>
          <point x="10" y="2"></point>
          <point x="-4" y="2"></point>
          <point x="12" y="5"></point>
          <point x="-7" y="4"></point>
          <point x="14" y="8"></point>
        </line>
        <line stroke="red">
          <point x="-10" y="5"></point>
          <point x="15" y="11"></point>
          <point x="-10" y="8"></point>
          <point x="14" y="15"></point>
          <point x="-10" y="11"></point>
          <point x="12" y="16"></point>
          <point x="-10" y="14"></point>
          <point x="12" y="19"></point>
          <point x="-10" y="17"></point>
          <point x="12" y="22"></point>
          <point x="-10" y="20"></point>
          <point x="12" y="25"></point>
          <point x="-10" y="23"></point>
          <!--<point x="" y=""></point>-->
        </line>
      </drawing>
      <actor t="translate(26,20) rotate(-15)" pose="-11,9|4,107|-11,99|-11,89|-18,78|-10,57|0,27|-5,2|17,29|15,15|-8,79|5,57|2,82|9,66">
        <bubble t="translate(9,11)" pose="0,0|-20,10|-81,49|14,94|-26,173|-100,127">
          <tspan x="0" y="-3em">Con los teroristas</tspan>
          <tspan x="0" y="0em">Wub wub wub wub wub wub, wub wub</tspan>
          <tspan x="0" y="1em">wub tas, wub wub, wub, wub wub wub</tspan>
          <tspan x="0" y="2em">Wub wub wub wub wub wub, wub wub wub </tspan>
          <tspan x="0" y="3em">tas wub wub, wub, wub tas wub wub tas</tspan>
          <tspan x="0" y="4em">tas wub tas wub tas</tspan>
          <tspan x="0" y="5em">wub tas wub tas tas</tspan>
        </bubble>
      </actor>
      <actor t="translate(140,22)" pose="29,1|7,103|28,69|28,59|28,71|28,51|17,32|17,2|33,26|38,1|19,45|18,64|38,52|36,30"></actor>
      <!-- table -->
      <drawing t="translate(100,10) rotate(3)">
        <line stroke="brown">
          <point x="0" y="0"></point><!-- bottom left leg bottom -->
          <point x="0" y="30"></point><!-- bottom left leg top -->
          <point x="50" y="50"></point><!-- top left leg top -->
          <point x="50" y="20"></point><!-- top left leg bottom -->
          <point x="50" y="50"></point><!-- top left leg top -->
          <point x="140" y="50"></point><!-- top right leg top -->
          <point x="140" y="20"></point><!-- top right leg bottom -->
          <point x="140" y="50"></point><!-- top right leg top -->
          <point x="90" y="30"></point><!-- bottom right leg top -->
          <point x="90" y="0"></point><!-- bottom right leg bottom -->
          <point x="90" y="30"></point><!-- bottom right leg top -->
          <point x="0" y="30"></point><!-- bottom left leg top -->
          <!-- end of wireframe - begin dodgy colouring in time -->
          <point x="55" y="50"></point>
          <point x="5" y="30"></point>
          <point x="60" y="50"></point>
          <point x="10" y="30"></point>
          <point x="65" y="50"></point>
          <point x="15" y="30"></point>
          <point x="70" y="50"></point>
          <point x="20" y="30"></point>
          <point x="75" y="50"></point>
          <point x="25" y="30"></point>
          <point x="80" y="50"></point>
          <point x="30" y="30"></point>
          <point x="85" y="50"></point>
          <point x="35" y="30"></point>
          <point x="90" y="50"></point>
          <point x="40" y="30"></point>
          <point x="95" y="50"></point>
          <point x="45" y="30"></point>
          <point x="100" y="50"></point>
          <point x="50" y="30"></point>
          <point x="105" y="50"></point>
          <point x="55" y="30"></point>
          <point x="110" y="50"></point>
          <point x="60" y="30"></point>
          <point x="115" y="50"></point>
          <point x="65" y="30"></point>
          <point x="120" y="50"></point>
          <point x="70" y="30"></point>
          <point x="125" y="50"></point>
          <point x="75" y="30"></point>
          <point x="130" y="50"></point>
          <point x="80" y="30"></point>
          <point x="135" y="50"></point>
          <point x="85" y="30"></point>
          <point x="140" y="50"></point>
          <point x="90" y="30"></point>
        </line>
      </drawing>
      <actor t="translate(102,-14) rotate(2)" pose="30,1|36,118|28,72|28,62|28,67|28,47|20,35|20,25|39,32|38,22|17,61|29,58|41,59|49,60"></actor>
      <actor t="translate(187,-4)" pose="31,1|6,128|27,82|27,72|28,71|28,51|10,42|12,23|21,49|23,28|15,63|2,61|37,66|39,53"></actor>
    </scene>
    <div id="scene2x">
    <scene id="scene2">
      <label t="translate(0,346)">
        <tspan x="0" y="0em">"And do the Harlem Shake"</tspan>
      </label>
      <drawing t="translate(0,31)">
        <line stroke="green">
          <point x="0" y="0"></point>
          <point x="250" y="20"></point>
        </line>
      </drawing>
      <!-- helmet; place behind head to cheat the circle -->
      <drawing t="translate(68,129) rotate(76)">
        <line stroke="black">
          <point x="0" y="0"></point>
          <point x="10" y="2"></point>
          <point x="-4" y="2"></point>
          <point x="12" y="5"></point>
          <point x="-7" y="4"></point>
          <point x="14" y="8"></point>
        </line>
        <line stroke="red">
          <point x="-10" y="5"></point>
          <point x="15" y="11"></point>
          <point x="-10" y="8"></point>
          <point x="14" y="15"></point>
          <point x="-10" y="11"></point>
          <point x="12" y="16"></point>
          <point x="-10" y="14"></point>
          <point x="12" y="19"></point>
          <point x="-10" y="17"></point>
          <point x="12" y="22"></point>
          <point x="-10" y="20"></point>
          <point x="12" y="25"></point>
          <point x="-10" y="23"></point>
          <!--<point x="" y=""></point>-->
        </line>
      </drawing>
      <actor t="translate(43,18) rotate(-15)" pose="-11,9|-19,119|-13,101|-13,91|-17,64|-6,58|5,29|0,4|21,30|19,16|-15,79|3,59|-3,74|14,68">
        <bubble t="translate(9,11)" pose="0,0|-20,10|-40,33|-8,51|-29,79|-86,157">
          <tspan x="0" y="0em">Wub wub wub wub wub wub, wub wub</tspan>
          <tspan x="0" y="1em">Shake</tspan>
          <tspan x="0" y="2em">Wub wub wub wub wub wub, wub wub wub </tspan>
          <tspan x="0" y="3em">Shake</tspan>
          <tspan x="0" y="4em">Wub Wub tas wub tas wub tas</tspan>
          <tspan x="0" y="5em">Con los teroristas</tspan>
          <tspan x="0" y="6em">Grrrrrrr</tspan>
        </bubble>
      </actor>
      <actor t="translate(137,14)" pose="29,1|23,147|29,96|29,86|34,92|36,67|25,48|25,18|41,42|46,17|4,88|2,111|46,103|53,136"></actor>
      <!-- table -->
      <drawing t="translate(100,10) rotate(3)">
        <line stroke="brown">
          <point x="0" y="0"></point><!-- bottom left leg bottom -->
          <point x="0" y="30"></point><!-- bottom left leg top -->
          <point x="50" y="50"></point><!-- top left leg top -->
          <point x="50" y="20"></point><!-- top left leg bottom -->
          <point x="50" y="50"></point><!-- top left leg top -->
          <point x="140" y="50"></point><!-- top right leg top -->
          <point x="140" y="20"></point><!-- top right leg bottom -->
          <point x="140" y="50"></point><!-- top right leg top -->
          <point x="90" y="30"></point><!-- bottom right leg top -->
          <point x="90" y="0"></point><!-- bottom right leg bottom -->
          <point x="90" y="30"></point><!-- bottom right leg top -->
          <point x="0" y="30"></point><!-- bottom left leg top -->
          <!-- end of wireframe - begin dodgy colouring in time -->
          <point x="55" y="50"></point>
          <point x="5" y="30"></point>
          <point x="60" y="50"></point>
          <point x="10" y="30"></point>
          <point x="65" y="50"></point>
          <point x="15" y="30"></point>
          <point x="70" y="50"></point>
          <point x="20" y="30"></point>
          <point x="75" y="50"></point>
          <point x="25" y="30"></point>
          <point x="80" y="50"></point>
          <point x="30" y="30"></point>
          <point x="85" y="50"></point>
          <point x="35" y="30"></point>
          <point x="90" y="50"></point>
          <point x="40" y="30"></point>
          <point x="95" y="50"></point>
          <point x="45" y="30"></point>
          <point x="100" y="50"></point>
          <point x="50" y="30"></point>
          <point x="105" y="50"></point>
          <point x="55" y="30"></point>
          <point x="110" y="50"></point>
          <point x="60" y="30"></point>
          <point x="115" y="50"></point>
          <point x="65" y="30"></point>
          <point x="120" y="50"></point>
          <point x="70" y="30"></point>
          <point x="125" y="50"></point>
          <point x="75" y="30"></point>
          <point x="130" y="50"></point>
          <point x="80" y="30"></point>
          <point x="135" y="50"></point>
          <point x="85" y="30"></point>
          <point x="140" y="50"></point>
          <point x="90" y="30"></point>
        </line>
      </drawing>
      <actor t="translate(112,26) rotate(2)" pose="30,1|32,156|16,114|16,104|8,92|8,72|19,62|14,33|29,65|26,39|5,103|22,80|29,101|37,94"></actor>
      <actor t="translate(182,33) rotate(5)" pose="31,1|59,117|23,99|23,89|20,73|27,60|7,46|16,22|17,50|24,30|12,73|3,75|33,86|5,85"></actor>
      <!-- helmet; place behind head to cheat the circle -->
      <drawing t="translate(130,124) rotate(-43)" pose="-2,-29">
        <line stroke="pink">
          <point x="0" y="0"></point>
          <point x="10" y="2"></point>
          <point x="-4" y="2"></point>
          <point x="12" y="5"></point>
          <point x="-7" y="4"></point>
          <point x="14" y="8"></point>
          <point x="-10" y="5"></point>
          <point x="15" y="11"></point>
          <point x="-10" y="8"></point>
        </line>
        <line stroke="green">
          <point x="14" y="15"></point>
          <point x="-10" y="11"></point>
          <point x="12" y="16"></point>
          <point x="-10" y="14"></point>
          <point x="12" y="19"></point>
          <point x="-10" y="17"></point>
          <point x="12" y="22"></point>
          <point x="-10" y="20"></point>
          <point x="12" y="25"></point>
          <point x="-10" y="23"></point>
          <!--<point x="" y=""></point>-->
        </line>
      </drawing>

      <actor t="translate(108,10)" pose="0,0|6,97|0,90|0,80|6,73|6,53|-10,33|-8,8|11,33|10,3|-10,70|-14,53|10,70|17,50"></actor>
      <actor t="translate(171,135) rotate(176)" pose="2,7|2,113|2,97|2,87|-9,94|3,71|-16,63|-9,34|15,55|24,48|-19,98|-23,124|27,98|30,130"></actor>
      <actor t="translate(226,18)" pose="0,0|0,89|0,82|0,72|0,70|0,50|-10,30|-10,5|13,30|11,6|-14,65|-12,50|17,66|19,50"></actor>
      <actor t="translate(225,18)" pose="-149,3|-148,96|-148,80|-148,70|-149,67|-149,47|-159,27|-153,-3|-139,27|-144,1|-166,77|-185,108|-132,62|-132,42"></actor>
      <actor t="translate(212,214)" pose="0,0|6,106|0,90|-13,64|10,71|12,55|20,49|35,25|25,75|33,59|-34,101|-42,81|26,86|35,114"></actor>
    </scene>
    </div>
    <div id="scene2y">
    <scene id="scene2b">
      <label t="translate(0,346)">
        <tspan x="0" y="0em">"And do the Harlem Shake"</tspan>
      </label>
      <drawing t="translate(0,31)">
        <line stroke="green">
          <point x="0" y="0"></point>
          <point x="250" y="20"></point>
        </line>
      </drawing>
      <!-- helmet; place behind head to cheat the circle -->
      <drawing t="translate(55,130) rotate(120)">
        <line stroke="black">
          <point x="0" y="0"></point>
          <point x="10" y="2"></point>
          <point x="-4" y="2"></point>
          <point x="12" y="5"></point>
          <point x="-7" y="4"></point>
          <point x="14" y="8"></point>
        </line>
        <line stroke="red">
          <point x="-10" y="5"></point>
          <point x="15" y="11"></point>
          <point x="-10" y="8"></point>
          <point x="14" y="15"></point>
          <point x="-10" y="11"></point>
          <point x="12" y="16"></point>
          <point x="-10" y="14"></point>
          <point x="12" y="19"></point>
          <point x="-10" y="17"></point>
          <point x="12" y="22"></point>
          <point x="-10" y="20"></point>
          <point x="12" y="25"></point>
          <point x="-10" y="23"></point>
          <!--<point x="" y=""></point>-->
        </line>
      </drawing>
      <actor t="translate(43,18) rotate(-15)" pose="-11,9|-40,108|-13,101|-13,91|10,80|-6,58|3,28|-2,3|21,30|19,16|-15,79|3,59|-3,74|14,68">
        <bubble t="translate(9,11)" pose="0,0|-20,10|-40,33|-8,51|-29,79|-60,174">
          <tspan x="0" y="0em">Wub wub wub wub wub wub, wub wub</tspan>
          <tspan x="0" y="1em">Shake</tspan>
          <tspan x="0" y="2em">Wub wub wub wub wub wub, wub wub wub </tspan>
          <tspan x="0" y="3em">Shake</tspan>
          <tspan x="0" y="4em">Wub Wub tas wub tas wub tas</tspan>
          <tspan x="0" y="5em">Con los teroristas</tspan>
          <tspan x="0" y="6em">Grrrrrrr</tspan>
        </bubble>
      </actor>
      <actor t="translate(137,14)" pose="29,1|14,145|29,96|29,86|34,92|36,67|25,48|25,18|41,42|46,17|4,88|-3,111|46,103|48,138"></actor>
      <!-- table -->
      <drawing t="translate(100,10) rotate(3)">
        <line stroke="brown">
          <point x="0" y="0"></point><!-- bottom left leg bottom -->
          <point x="0" y="30"></point><!-- bottom left leg top -->
          <point x="50" y="50"></point><!-- top left leg top -->
          <point x="50" y="20"></point><!-- top left leg bottom -->
          <point x="50" y="50"></point><!-- top left leg top -->
          <point x="140" y="50"></point><!-- top right leg top -->
          <point x="140" y="20"></point><!-- top right leg bottom -->
          <point x="140" y="50"></point><!-- top right leg top -->
          <point x="90" y="30"></point><!-- bottom right leg top -->
          <point x="90" y="0"></point><!-- bottom right leg bottom -->
          <point x="90" y="30"></point><!-- bottom right leg top -->
          <point x="0" y="30"></point><!-- bottom left leg top -->
          <!-- end of wireframe - begin dodgy colouring in time -->
          <point x="55" y="50"></point>
          <point x="5" y="30"></point>
          <point x="60" y="50"></point>
          <point x="10" y="30"></point>
          <point x="65" y="50"></point>
          <point x="15" y="30"></point>
          <point x="70" y="50"></point>
          <point x="20" y="30"></point>
          <point x="75" y="50"></point>
          <point x="25" y="30"></point>
          <point x="80" y="50"></point>
          <point x="30" y="30"></point>
          <point x="85" y="50"></point>
          <point x="35" y="30"></point>
          <point x="90" y="50"></point>
          <point x="40" y="30"></point>
          <point x="95" y="50"></point>
          <point x="45" y="30"></point>
          <point x="100" y="50"></point>
          <point x="50" y="30"></point>
          <point x="105" y="50"></point>
          <point x="55" y="30"></point>
          <point x="110" y="50"></point>
          <point x="60" y="30"></point>
          <point x="115" y="50"></point>
          <point x="65" y="30"></point>
          <point x="120" y="50"></point>
          <point x="70" y="30"></point>
          <point x="125" y="50"></point>
          <point x="75" y="30"></point>
          <point x="130" y="50"></point>
          <point x="80" y="30"></point>
          <point x="135" y="50"></point>
          <point x="85" y="30"></point>
          <point x="140" y="50"></point>
          <point x="90" y="30"></point>
        </line>
      </drawing>
      <actor t="translate(112,26) rotate(2)" pose="30,1|38,137|14,101|14,91|8,92|8,72|15,53|10,24|25,54|22,28|3,90|20,67|27,88|31,67"></actor>
      <actor t="translate(182,33) rotate(5)" pose="31,1|38,138|23,99|23,89|24,73|31,60|11,46|13,20|21,50|25,26|12,73|3,75|33,86|7,73"></actor>
      <!-- helmet; place behind head to cheat the circle -->
      <drawing t="translate(116,127) rotate(10)" pose="-2,-29">
        <line stroke="pink">
          <point x="0" y="0"></point>
          <point x="10" y="2"></point>
          <point x="-4" y="2"></point>
          <point x="12" y="5"></point>
          <point x="-7" y="4"></point>
          <point x="14" y="8"></point>
          <point x="-10" y="5"></point>
          <point x="15" y="11"></point>
          <point x="-10" y="8"></point>
        </line>
        <line stroke="green">
          <point x="14" y="15"></point>
          <point x="-10" y="11"></point>
          <point x="12" y="16"></point>
          <point x="-10" y="14"></point>
          <point x="12" y="19"></point>
          <point x="-10" y="17"></point>
          <point x="12" y="22"></point>
          <point x="-10" y="20"></point>
          <point x="12" y="25"></point>
          <point x="-10" y="23"></point>
          <!--<point x="" y=""></point>-->
        </line>
      </drawing>

      <actor t="translate(108,10)" pose="0,0|6,97|0,90|0,80|0,70|0,50|-10,30|-5,3|10,30|10,0|-10,70|-10,50|10,70|10,50"></actor>
      <actor t="translate(171,135) rotate(176)" pose="6,7|6,113|6,97|6,87|-8,94|4,71|-15,63|-13,36|14,51|19,41|-15,98|-19,124|31,98|34,130"></actor>
      <actor t="translate(226,18)" pose="0,0|0,89|0,82|0,72|0,70|0,50|-10,30|-4,5|13,30|8,6|-13,63|-7,50|15,63|17,47"></actor>
      <actor t="translate(225,18)" pose="-149,3|-149,109|-149,93|-149,83|-149,73|-149,53|-159,33|-159,3|-139,33|-139,3|-167,90|-176,121|-139,73|-139,53"></actor>
      <actor t="translate(212,214)" pose="0,0|0,106|0,90|-13,64|10,71|12,55|20,49|35,25|25,75|33,59|-34,101|-38,135|26,86|35,114"></actor>
    </scene>
    </div>
    
    <scene id="scene3" height="160">
      <label t="translate(-2,188)" pose="0,-10|7,-30">
        <tspan x="0" y="0em">Non Web Addicts</tspan>
      </label>
      <actor t="translate(111,7) rotate(2)" pose="-41,48|-10,105|0,88|0,78|0,68|0,48|-5,23|-10,-2|5,23|10,-2|-11,70|-4,54|17,86|-4,110">
        <bubble t="translate(88,-55)" pose="-12,5|-111,42|-144,29|-129,57|-159,64|-177,75">
          <tspan x="0" y="0em" fill="red">WTF?!</tspan>
        </bubble>
      </actor>
    </scene>
    <scene id="scene4" width="300" height="150" margin-y="3" frame="no">
      <label t="translate(11,133)" pose="-10,14|-9,5">
        <tspan x="0" y="0em">~ comix markup</tspan>
        <tspan x="0" y="1em">~ can be mixed with HTML</tspan>
        <tspan x="0" y="2em">~ WYSIWYG editor</tspan>
        <tspan x="0" y="3em">~ open-source</tspan>
        <tspan x="0" y="4em">~ backed by </tspan><tspan fill="blue">gist.github.com</tspan>
        <tspan x="0" y="5em">~ xkcd harlem shake by </tspan><tspan fill="green">JohnLBevan</tspan>
      </label>
      <actor t="translate(211,44) rotate(-4)" pose="73,-56|77,58|79,38|87,22|81,12|82,1|63,-18|57,-40|89,-14|93,-38|69,23|51,30|71,15|43,19">
      </actor>
    </scene>
  </div>
</body>
</html>

2012-10-12

Path Finder in C#

Filed under: Microsoft, Technology — Tags: , , , — Developer42 @ 21:58

A recent question on Stack Overflow asked how to write recursive code to navigate a maze. Here’s my attempt at a solution (written in C#).

//Program.cs
using System;
using System.Collections.Generic;

namespace PathFinder
{
    class Program
    {
        static void Main(string[] args)
        {
            new Program();
            Console.WriteLine("Done");
            Console.ReadKey();
        }

        private Program()
        {
            Maze maze = new Maze
                (
                    new bool[4, 4]
                {
                    {true,false,true,false}
                    ,{true,true,true,true}
                    ,{false,true,false,true}
                    ,{false,true,true,true}
                }
                );
            
            foreach (IEnumerable<Point> path in maze.FindPaths(0, 0, 3, 3))
            {
                OutputPath(path);
            }

        }


        private static void OutputPath(IEnumerable<Point> path)
        {
            Console.WriteLine();
            foreach (Point p in path)
            {
                Console.Write(p.ToString());
            }
            Console.WriteLine();
        }
    }
}
//Maze.cs
using System.Collections.Generic;
using System.Linq;

namespace PathFinder
{
    public class Maze
    {
        bool[,] maze;
        public Maze(bool[,] maze)
        {
            this.maze = maze;
        }

        private int MaxPathLength
        {
            get { return this.maze.Length; }
        }

        private bool IsOnPath(Point p)
        {
            return maze[p.X, p.Y];
        }

        public IEnumerable<IEnumerable<Point>> FindPaths(int startX, int startY, int endX, int endY)
        {
            Point start = new Point(startX, startY, 0, 0, this.maze.GetLength(0)-1, this.maze.GetLength(1)-1);
            Point end = new Point(endX, endY);
            if (!start.IsOnGrid) throw new OffGridException(start);
            if (!end.IsOnGrid) throw new OffGridException(end);
            if (!IsOnPath(start)) throw new OffPathException(start);
            if (!IsOnPath(end)) throw new OffPathException(end);
            return Step(start, end, this.MaxPathLength, Direction.StandStill);
        }
        private IEnumerable<IEnumerable<Point>> Step(Point current, Point end, int stepsRemaining, Direction direction)
        {
            if (stepsRemaining > 0 && current.IsOnGrid && IsOnPath(current))
            {
                stepsRemaining--;
                if (current.Equals(end))
                {
                    yield return new[] { current };
                }
                else
                {
                    //could be improved by threading (parallel.foreach won't work though due to use of yield return)
                    foreach (Direction d in Direction.Directions)
                    {
                        if (!direction.IsOppositeOf(d))
                        {
                            foreach (IEnumerable<Point> path in Step(d.Move(current), end, stepsRemaining, d))
                            {
                                yield return (new[] { current }).Concat(path);
                            }
                        }
                    }
                }
            }
        }
    }
}
//Point.cs
namespace PathFinder
{

    public struct Point
    {
        const string DisplayFormat = "({0},{1})";

        public static int MaxX { get; set; }
        public static int MaxY { get; set; }
        public static int MinX { get; set; }
        public static int MinY { get; set; }

        public int X { get; set; }
        public int Y { get; set; }
        public Point(int x, int y)
            : this()
        {
            this.X = x;
            this.Y = y;
        }
        public Point(int x, int y, int minX, int minY, int maxX, int maxY)
            : this(x, y)
        {
            MinX = minX;
            MinY = minY;
            MaxX = maxX;
            MaxY = maxY;
        }

        public bool IsOnGrid
        {
            get
            {
                return this.X <= MaxX
                    && this.X >= MinX
                    && this.Y <= MaxY
                    && this.Y >= MinY;
            }
        }

        public override string ToString()
        {
            return string.Format(DisplayFormat, this.X, this.Y);
        }

    }
}
//Direction.cs
using System.Collections.Generic;

namespace PathFinder
{

    //thanks to Roelof for this trick (http://stackoverflow.com/questions/10704863/c-sharp-ensure-valid-enum-values-futureproof-method)
    class Direction
    {
        private string name;
        private int horizontalMovement;
        private int verticalMovement;

        private Direction(int horizontalMovement, int verticalMovement, string name)
        {
            this.horizontalMovement = horizontalMovement;
            this.verticalMovement = verticalMovement;
            this.name = name;
        }

        public static readonly Direction StandStill = new Direction(0, 0, "Still");//don't include this in directions

        public static readonly Direction Left = new Direction(-1, 0, "Left");
        public static readonly Direction Right = new Direction(1, 0, "Right");
        public static readonly Direction Up = new Direction(0, 1, "Up");
        public static readonly Direction Down = new Direction(0, -1, "Down");
        //if required we could add diagonals / jumps (i.e. values > 1 or <-1)
        //if we add anything above, also add to the GetDirections list - that way all logic will be auto applied
        public static readonly IEnumerable<Direction> Directions = new Direction[] { Left, Right, Up, Down };

        public bool IsOppositeOf(Direction d)
        {
            return
                (this.horizontalMovement + d.horizontalMovement) == 0
                &&
                (this.verticalMovement + d.verticalMovement) == 0;
        }

        public Point Move(Point from)
        {
            return new Point(from.X + this.horizontalMovement, from.Y + this.verticalMovement);
        }

        public override string ToString()
        {
            return this.name;
        }
    }
}
//OffGridException.cs
using System;

namespace PathFinder
{
    class OffGridException: Exception
    {

        const string ExceptionText = "Provided point ({0},{1}) is outside the bounds of the grid (({2},{3}),({4},{5})).";
        
        public OffGridException(Point p)
            :base(string.Format(ExceptionText,p.X,p.Y,Point.MinX, Point.MinY, Point.MaxX, Point.MaxY))
        {}
    }
}
//OffPathException.cs
using System;

namespace PathFinder
{
    class OffPathException : Exception
    {

        const string ExceptionText = "Provided point ({0},{1}) is not located on a path in the maze.";

        public OffPathException(Point p)
            : base(string.Format(ExceptionText, p.X, p.Y))
        { }
    }
}

2012-08-21

Run VBScript from SQL

Filed under: Microsoft, SQL Server, Technology — Tags: , , , — Developer42 @ 18:13

Though it’s fairly well publicised how to call an existing VBS file from SQL, or how to execute VBScript within a SQL agent, there’s not much info out there on how to have SQL run VBS directly. The code below allows you to manage your scripts in a database table, creating temp files to host and run the scripts as required. The sp_RunSQL stored proc can also be used to run any VBS script; including dynamically generated code.
As always, feedback, suggestions, etc are welcome.

Code:

    use [insert-db-name-here]
    go
    ----use below code to enable commands (required for xp_cmdshell to work)
    --exec sp_configure 'show advanced options', 1
    --go
    --reconfigure
    --go
    --exec sp_configure 'xp_cmdshell', 1  
    --go
    --reconfigure
    --go
    if OBJECT_ID('vbsScripts') is not null drop table vbsScripts
    go
    create table vbsScripts 
    (
        id bigint not null identity(1,1) constraint pk_vbsScripts primary key clustered 
        , name nvarchar(256) not null constraint uk_vbsScripts_name unique
        , script nvarchar(max) not null
        , timeoutSecs int null constraint df_vbsScripts_timeoutSecs default(86400)--leave as null if you don't want a timeout / defaults to 1 day / 24*60*60
        , batchMode bit not null constraint df_vbsScripts_batchMode default(1)
    )
    go
    /*
    Gets the temp directory from environment variables
    usage:
        declare @tempPath nvarchar(max)
        exec GetTempDirectory @tempPath out 
        select @tempPath 
    */
    if OBJECT_ID('GetTempDirectory') is not null drop proc GetTempDirectory
    go
    create proc GetTempDirectory(@path nvarchar(max) out)
    as
    begin
        set @path = ''
        declare @tempTable table(data nvarchar(max))
        insert @tempTable exec master..xp_cmdshell 'echo %temp%'
        select top 1 @path = data from @tempTable
        if SUBSTRING(@path,len(@path),1)  '\' set @path = @path + '\'
    end
    go
    /*
    Creates a unique filename (using guid to ensure uniqueness and datetime to make the name friendlier)
    usage:
        declare @tempPath nvarchar(max)
        exec GetTempDirectory @tempPath out 
        select @tempPath 
    */
    if OBJECT_ID('GetTempFilename') is not null drop proc GetTempFilename
    go
    create proc GetTempFilename(@fn nvarchar(max) out)
    as
    begin
        --exec GetTempDirectory @fn out --can just use environment variable - originally had issues testing as was looking at the wrong user's temp directory :/
        --set @fn = @fn + 'sqlTemp_' + replace(replace(replace(convert(nvarchar(24), getutcdate(),127),'-',''),':',''),'.','') + '_' + CAST(NEWID() as nvarchar(36)) + '.tmp'
        set @fn = '%temp%\' + 'sqlTemp_' + replace(replace(replace(convert(nvarchar(24), getutcdate(),127),'-',''),':',''),'.','') + '_' + CAST(NEWID() as nvarchar(36)) + '.tmp'
    end
    go
    if OBJECT_ID('dbo.fn_EscapeDosCharachters') is not null drop function dbo.fn_EscapeDosCharachters
    go
    create function dbo.fn_EscapeDosCharachters
    (
        @text nvarchar(max)
    )
    returns nvarchar(max)
    as
    begin
        --http://www.dostips.com/?t=Snippets.Escape
        set @text = REPLACE(@text,'^','^^')
        set @text = REPLACE(@text,'!','^!')
        set @text = REPLACE(@text,'&amp;','^&amp;')
        set @text = REPLACE(@text,'|','^|')
        set @text = REPLACE(@text,'%','%%')

	--based on experience
	set @text = REPLACE(@text,'&gt;','^&gt;')
	set @text = REPLACE(@text,'&lt;','^&lt;')
        return @text
    end
    go
    if OBJECT_ID('createTempTextFile') is not null drop proc createTempTextFile
    go
    create proc createTempTextFile
    (
        @fn nvarchar(max) out 
                          --the filename to output to (nb: environment variables don't currently work (e.g. you can't use %temp%\myFile.vbs)
                          --works fine with spaces in filename (so far at least)
                          --if user passes null a temporary filename will be auto allocated &amp; returned in this variable
        , @content nvarchar(max)
    )
    as
    begin

        declare @charPos int
        , @cmd varchar(8000) --has to be varchar rather than nvarchar due to xp_cmdshell implementation

        if @fn is null or LEN(@fn)=0
        begin
            exec GetTempFilename @fn out
        end

        set @cmd = '@echo.&gt;' + @fn --create a new file for our script output
        EXEC master..xp_cmdshell @cmd, no_output

        set @content = replace(@content,char(13) + char(10), char(10))--ensure uniform line endings (i.e. \r\n -&gt; \n)
        set @content = replace(@content,char(13), char(10))--ensure uniform line endings (i.e. \r -&gt; \n)
        set @content = @content + CHAR(10) --ensure last character of script is new line
        set @charPos = CHARINDEX(char(10),@content)
        while (@charPos &gt; 0)
        begin
            --todo: consider what additional escaping is required to prevent injection issues
            set @cmd = '@echo.' + dbo.fn_EscapeDosCharachters(SUBSTRING(@content,1,@charPos-1)) + '&gt;&gt; ' + @fn
            EXEC master..xp_cmdshell @cmd, no_output
            set @content = SUBSTRING(@content,@charPos+1,len(@content))
            set @charPos = CHARINDEX(char(10),@content)
        end

    end 
    go
    if OBJECT_ID('deleteTempTextFile') is not null drop proc deleteTempTextFile
    go
    create proc deleteTempTextFile
    (
        @fn nvarchar(max)
    )
    as
    begin
        declare @cmd varchar(8000)
        if CHARINDEX(' ',@fn)&gt;0 and CHARINDEX('&quot;',@fn)&gt;1 set @fn = QUOTENAME(@fn,'&quot;')
        set @cmd = 'del ' + @fn
        EXEC master..xp_cmdshell @cmd, no_output
    end
    go
    if OBJECT_ID('sp_RunScript') is not null drop proc sp_RunScript
    go
    create proc sp_RunScript
    (
        @script nvarchar(max)
        , @arguments nvarchar(max)
        , @timeoutSecs int = null
        , @batchMode bit = 1
        , @tempfileUri nvarchar(max) out
    )
    as
    begin
    
        declare @cmd varchar(8000) --has to be varchar rather than nvarchar due to xp_cmdshell implementation
        
        exec createTempTextFile @tempfileUri out, @script
        
        if CHARINDEX(' ',@tempfileUri)&gt;0 and CHARINDEX('&quot;',@tempfileUri)&gt;1 set @tempfileUri = QUOTENAME(@tempfileUri,'&quot;')
        set @cmd = 'cscript ' + @tempfileUri + ' //E:vbscript //NOLOGO '
        
        --batch mode or interactive
        if @batchMode=1 
            set @cmd = @cmd + '//B '
        else
            set @cmd = @cmd + '//I '
        
        --should script timeout after x seconds?
        if @timeoutSecs is not null
            set @cmd = @cmd + '//T:' + CAST(@timeoutSecs as nvarchar(18)) + ' '
        
        set @cmd = @cmd + isnull(@arguments,'')
        --select @cmd --if debugging enable this line to see the script file / etc
        
        EXEC master..xp_cmdshell @cmd --if required we can capture output as has been done in GetTempDirectory
        
        exec deleteTempTextFile @tempfileUri --tidyup the temp script - disable this line for debugging
            
    end
    if OBJECT_ID('sp_RunScriptByID') is not null drop proc sp_RunScriptByID
    go
    create proc sp_RunScriptByID
    (
        @scriptId bigint
        , @arguments nvarchar(max)
    )
    as
    begin
    
        declare @timeoutSecs int
        , @batchMode bit
        , @script nvarchar(max)
        , @tempfileUri nvarchar(max)
        , @cmd varchar(8000) --has to be varchar rather than nvarchar due to xp_cmdshell implementation
            
        select @timeoutSecs=timeoutSecs 
        , @batchMode = batchMode
        , @script = script
        from vbsScripts 
        where id = @scriptId
        
        exec sp_RunScript  
			@script
			, @arguments 
			, @timeoutSecs 
			, @batchMode 
			, @tempfileUri out 
        
    end
    go
    if OBJECT_ID('sp_RunScriptByName') is not null drop proc sp_RunScriptByName
    go

    /*
    provides a friendly interface to sp_RunScriptByID
    */
    create proc sp_RunScriptByName
    (
        @scriptName nvarchar(256)
        , @arguments nvarchar(max)
    )
    as
    begin

        declare @id bigint

        select @id = id
        from vbsScripts 
        where name = @scriptName

        exec sp_RunScriptByID @id, @arguments

    end
    go

Example Usage:

    --demo

    --register a new script in the scripts table
    insert vbsScripts 
    select 'demo', '
    option explicit
    dim objFSO, i, path
    path = "c:\example1\"
    wscript.echo "hello" ''show what console output looks like (if interactive)
    for i = 0 to wscript.Arguments.Count-1 ''show that we can handle command line arguments
        wscript.echo wscript.arguments.item(i)
    next
    set objFSO = CreateObject("Scripting.FileSystemObject")    
    if not objFSO.FolderExists(path) then
		on error resume next
        objFSO.CreateFolder(path) ''create a folder to demonstrate that the vbs is running / affecting the outside environment
        if err.number = 0 then
			wscript.echo "Folder " &amp; path &amp; " successfully created."
		else
			wscript.echo "Folder " &amp; path &amp; " was not created.  " &amp; cstr(Err.number) &amp; ": " &amp; Err.Description
			err.clear
		end if
        on error goto 0
    else
        wscript.echo "Folder " &amp; path &amp; " already exists."
    end if
    set objFSO = Nothing
    wscript.echo "Done"
    ', null, 0
    go

    --execute above script via friendly name
    sp_RunScriptByName 'demo','"this is a demo" "hopefully it will work" yes it does'

2011-12-09

SQL Optimisation :: Convert Primary Keys to Clustered Indexes

We recently spotted that one of the systems we use did not make use of clustered indexes on any tables out of the box. As a result performance was not as good as it could have been. The below script allows for an easy win optimisation by finding all tables which do not include a clustered index, and converting the table’s primary key to be a clustered index.

In a future post I’ll put up more details on what clustered indexes are, why you should always (pretty much) use them and other useful info for anyone playing with databases.

--This script is designed for MS SQL Server
use DbNameToOptimise
go

--disable all constraints on all tables (to avoid these causing errors when altering the indexes)
sp_msforeachtable 'alter table ? nocheck constraint all'
go
 
declare @sqls table(object_id bigint, sort int, sql nvarchar(max))
 
insert @sqls
select t.object_id, ic.key_ordinal, case when ic.key_ordinal=1 then  'CREATE UNIQUE CLUSTERED INDEX [' + i.name + '] ON [' + t.name + ']([' else ',[' end + c.name + case when ic.key_ordinal=icagg.maxko then ']) WITH DROP_EXISTING' else ']' end sql
from sys.tables t
inner join sys.indexes i on t.object_id = i.object_id
inner join sys.index_columns ic on i.object_id=ic.object_id and  i.index_id = ic.index_id
inner join sys.columns c on ic.object_id = c.object_id and ic.column_id = c.column_id
inner join (
      select object_id, index_id, MAX(key_ordinal) maxko from sys.index_columns group by object_id,index_id
) icagg on i.object_id = icagg.object_id and i.index_id = icagg.index_id
where t.is_ms_shipped=0
and i.is_primary_key=1
and not exists (
      --ignore tables which already have a clustered index
      select 1
      from sys.indexes i2
      where t.object_id = i2.object_id
      and i2.type = 1
)
order by t.name, i.name, ic.key_ordinal
 
declare @objid bigint
, @sql nvarchar(max)
 
while exists (select top 1 1 from @sqls)
begin
      set @sql=''
      select top 1 @objid=object_id from @sqls
      select @sql = @sql + sql from @sqls where object_id=@objid order by sort
      delete from @sqls where object_id = @objid
      exec (@sql)
end
 
go

--reenable constraints to leave the db as we found it (aside from the fix)
sp_msforeachtable 'alter table ? check constraint all'
go

2011-06-28

A Request / Random Thoughts

As with many of my posts, this is basically an unedited brain dump – apologies. Hopefully this will encourage some interesting comments / discussion though. . .

A Standard for Developer APIs
Facebook, Twitter, Worpress (and I’m sure Google+ will) offer APIs to developers allowing them to pull data out of their applications and manipulate it as they like. Most of these services offer similar functions; authentication, get the last x posts, pull back a grid of contacts, etc. All do this in their own way.
What would be great is some unification – either a library over the top of the existing APIs to pull them all in line, or for some set of social standards to be formed in the same way Netscape, IE, Mozilla and more came up with ECMAScript as a way to allow javascript to become portable. What I’m hoping for is something like this:

//this is entirely made up code - not (yet) some awesome new Google thing 
var application = GetApplication('Google+'); //creates a new object with an "application interface" for Google+
if (application.authenticate('Developer42','DemoCodePassword') //authenticate a user against the web app
{
    var identity = application.me(); //pull back an object which represents me
    var allFriends = me.ListContacts(); //by default pull back all contacts
    var colleagues = me.ListContacts('colleagues'); //or filter by group
    var posts = identity.GetPosts(20); //get my last 20 posts
    var friendsPosts = allFriends.GetPosts(100); //Get the last 100 posts by my friends/contacts
}

A Service for Services
This is probably what the guys who came up with UDDI were thinking:

If two companies offer a service to give out exchange rate information, and both use the same standard, when I want to get back exchange rate info why can’t I just post a request to the web saying “give me the exchange rate from USD to GBP” and have it chuck back .67 without all the hassle of searching for a suitable service.

There’s a whole bunch of data which we often need, but have to trawl the web for. Search engines began to make this better, WolframAlpha got a bit closer, but no one’s yet cracked it. What I’d like is a single web site containing a catalogue of services and their schemas. I pick a service, write code to its schema, then use the service url to pull back this data. From my point of view I’m just pulling data from http://www.UsefulServices.com/ExchangeRates, but in the background that could be talking to any (approved) provider. I guess the reason this doesn’t yet exist is the issue around monetisation; but surely there’s a way. . . ?
Below’s my wish list of services:

- Exchange Rates
- Share Prices
- National Holidays
- Daylight Savings Dates
- Post Code / Geo (long & lat) Conversion
- Credit Checks
- Product Prices
- Companies House Info

2011-03-28

Button to spell check a web-page in Google Chrome

Filed under: Google, Technology — Developer42 @ 20:32

A question recently popped up on the Chromium discussion forum asking if there was a way to get Chrome to apply its spell check to a whole website, as opposed to just a text box. Though I don’t know of one, I thought of a little javascript workaround. Copying and pasting the code below into your address bar will copy the text content of the website into a textarea causing Chrome to automatically spell check the contents. Not ideal, but nice for anyone who enjoys scripts.

javascript:var ta=document.createElement('textarea'); var s=document.createAttribute('style'); s.nodeValue='width:100%;height:100em;'; ta.setAttributeNode(s); ta.appendChild(document.createTextNode(document.body.innerText)); document.body.appendChild(ta); ta.focus(); for(var i=1;i&lt;=ta.value.length;i++)ta.setSelectionRange(i,i);

Useful resource: Getting & setting the caret (text cursor) position
Original Question: Q. Button to spell check a web-page

2010-06-29

My First Python Plugin

Filed under: Technology, WordPress — Tags: , , , , , , , , — Developer42 @ 22:51

I’ve read a lot about Python in the past, but have been so inundated with things to learn recently that it had always fallen to the bottom of the pile. However, recently I requested a feature for my favourite text editor, Programmers’ Notepad, and heard back from its developer, Simon, within moments of submitting my request. He’d provided a solution and instructions on how to implement it, which introduced me to a powerful set of features I’d previously been unaware of (namely that you could run apps which output to the command line straight from the tools menu, and have their output returned into the current document). After realising I’d been missing out on some of this potential, I spent a bit of time going over the pnotepad.org site, seeing what else I’d missed. This is where I come back to Python. PNotepad allows you to write custom scripts in Python, put them into pn’s \scripts directory, and run them from the scripts toolbar the next time you start up the editor. I figured any time spent learning python would easily be offset by the hours saved by having scripts take care of laborious tasks for me, so promptly downloaded and installed Python and PyPN (which enables Python scripts in PN), had a look at a script from the script repository, and a browse through the python manual, and then wrote my first script in a few minutes. For anyone wanting to see it, my script is a simple tool for replacing < > and & characters with their HTML codes, and inserting the PRE and CODE tags required to publish code to wordpress. I’ve uploaded it to the script share, here.

First Impressions
My first impressions of Python, after having coded for all of 20 mins or so, are that it’s a really simple language to learn, which anyone (including non developers) should be able to pick up pretty quickly. There are a few bits I’m not so keen on; select statements don’t exist, using instead piles of else-if (elif) statements, and not having curly brackets and semi-colons, but instead relying on indentation leaves me feeling a little out of my comfort zone, but all this is just a familiarity thing, which I’m sure I’ll soon get over. I’m hoping to write a few more scripts over the next few weeks (assuming I can find interesting and required things to write), and to learn a bit more about Python outside of PN, after which I’ll report back on my experiences for anyone else interested in heading in that direction.

Steps Taken to Run Python in PN
If you’d also like to get into Python for PN, here’s a step by step set of instructions on what to do (skipping a few steps to avoid typing “click next” too many times).
Download & install/extract the following, in the order listed below:

Once installed, go to the command line (windows key + r, cmd, enter), navigate to the programmer’s notepad directory (cd %programfiles%\programmer*), then type the following “pn –findexts”. It looks like nothing’s happened, but that’s good; it hasn’t errorred.
Next, load up programmer’s notepad, click view, windows, scripts (or press alt+F10), and you should see a list of available scripts.
Get an existing script from the script share site: http://scriptshare.rocketmonkeys.com/ and save the file to your …\Programmer’s Notepad\scripts\ directory.
Close and reopen PN. You should now see a new script (or several) listed in the scripts toolbar.
From here on, it’s pretty easy to figure out what to do to create your own scripts by looking at existing scripts and python code, and playing.
Good luck.

Older Posts »

The WordPress Classic Theme. Create a free website or blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.

Join 648 other followers

%d bloggers like this: