Programming, Python

Day 1: Learning Python

This is a progress report of my Python learning. This is not to teach you how to code Python, this is merely the results of my Python homework. For my first lesson, I had to come up with a way to program a memory game in Python, here are my results.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# Board Definitions
board         = ['A', 'B', 'C', 'D', 'E'] * 2
board_visible = list("__________")
board_flipped = []
board_steps   = 0

# Shuffle the board
import random
random.shuffle(board)

def printBoard(board):
    ''' Print the board '''
    print(" ".join(board))
    print("0 1 2 3 4 5 6 7 8 9")

def boardComplete(board):
    ''' Check if board is completed '''
    return not '_' in board

def inRange(value):
    ''' Check if value is digit '''
    if value.isdigit():
        test = int(value)
        ''' Ensure value is in range '''
        if test  9:
            ''' Out of range error '''
            print("Input out of range!")
            return False

        ''' Valid input, return true '''
        return True

    ''' Not an integer, ignore input '''
    return False

def quitApp(value):
    ''' Check if quit has been requested '''

    if value.isdigit():
        ''' Input is an integer, ignore it '''
        return False

    ''' Check if character is our quit character '''
    if value == 'q' or value == 'Q':
        return True

    ''' Illegal character '''
    print("Invalid option!")
    return False

def flipTile(idx):
    if board_visible[idx] == '_':
        board_visible[idx] = board[idx]
        board_flipped.append(idx)
        return True
    return False


''' Start Application '''
while True:
    # Show board and board key
    printBoard(board_visible)

    # check if we have one
    if boardComplete(board_visible):
        print("You win in {:d} moves!" . format(board_steps))
        break

    if len(board_flipped) == 2:
        a, b = [board[i] for i in board_flipped]
        if a == b:
            # don't flip if they match
            board_flipped = []
        else:
            for i in board_flipped:
                # flip cards back if they don't match
                board_visible[i] = '_'
                board_flipped    = []


    # get user input
    user_input = input("Input: ")

    if quitApp(user_input):
        break

    # check input type
    if inRange(user_input):
        idx = int(user_input)

        if flipTile(idx):
            board_steps = board_steps + 1
        else:
            print("Tile already flipped!")

Nothing too special, but it gets the job done. Just a simple 10 card memory game. I had to show that I’ve learned and understood the basic concepts taught by the course, so I may have done some things that might not be optimum. I could have done this whole game in less than 65 lines of code and gotten the same results, but it would not have demonstrated all that I had learned as part of the course.

Software

PHP Namespace Rant

In planning my PHP projects, I continually run into the same issue. Roughly ninety percent of the best PHP libraries out there do not use namespaces. This is of course a result of namespaces being relatively new, and many projects wanting to maintain compatibility with PHP 5.2 and earlier. What this really translates into for me is, if I want to add a library to one of my projects, which are designed with PHP 5.4+ in mind, I have to manually namespace all the files in that library, else face likely code conflicts.

Now, I may not have any conflicts with the working files, if I include them into the root namespace, until I start trying to autoload. Every large PHP library seems to have its own autoloader, which is fine, until you realize that they are almost always named autoloader. This of course is the first conflict that I almost always run into. I can load multiple autoloaders, but I can’t do so if they all have the same name and the same namespace. The simplest solution is to give them all the same namespace of the vendor. This does work, but it’s a bit messy. The more difficult solution would be to implement the third party autoloader into my own autoloader. This is just too much of a pain in the ass.

You may think that at this point my solution is simple. It’s not, it’s messy, it’s ugly, and it’s downright disgusting. By doing modifying the files I run into issues when it comes to using version control. I am moving all my projects over to git, due to one of it’s awesome features of using submodules. By using submodules, I can include the repository of another library in my project, and have it keep up to date, without having to manually update it myself. The second I modify one of the files, my only option is to fork the project. What this means is, whenever a project is updated, I have to re-pull all the files downstream, and then do my modifications all over again. This is a glorious waste of time and effort. I chose those libraries because I feel that the vendors of those libraries are the best at what they do, and I don’t want to try and reproduce their work all over again. Namespacing non-namespaced code defeats that purpose, forcing me to fork their code, and hope that I don’t miss any major security updates as time goes on.

As you can see, namespacing is awesome, if everyone uses it. So long as everyone tries to be backwards compatible, and produces non-namespaced code, namespacing is a headache. It’s at this point that people will say, why not just get rid of namespaces. I will tell them, that is not the solution. Without namespaces, the conflicts I had will still be there. Namespacing fixes the conflicts. Namespacing gives code context. Context in any language, programmed or spoken, is important. Context does more than tell us what you said, context tells us what you mean. Namespaces are a step forward, the next step is to actually use them.

IRC, Software

Toshiko IRC Bot

Toshiko is a horrible IRC Bot. But she is absolutely wonderful, when you understand why I created her. Toshiko is my attempt at learning the Ruby programming language. For me, the best way to learn anything is to apply it toward something that I enjoy. Now before you get too impressed, I’m not starting from ground zero. What little I know about ruby stems from my experience working with RPG Maker’s RGSS, which is essentially a stripped down version of Ruby with custom game libraries stacked in an embedded scripting environment. It’s a good base.

I had contemplated doing this project in python, however, I decided to not push myself so hard and to work with something that was already familiar to me at some level. Advanced Ruby programmers will probably have lot’s of comments about what I’m doing wrong in my code. That’s fine, I know that I’m not an expert in this language. My knowledge of this language is laughable at best. If you see me doing something that’s genuinely bad, or that I could be doing better, let me know. I’m doing this to learn, I accept the fact that I’ll make horrible mistakes and errors along the way. I also appreciate any advice that I can get.

Alright, now that I have gotten the introduction out of the way, let me show you my script. It’s not fully functional yet, it only idles, but the essentials are there. You’ll notice that I’ve included some stuff that I’m not using yet, such as the thread library. That’s because, at the time of posting, I am getting ready to implement threading. I’m doing some research on it now, and only included the dependency so that I wouldn’t forget once I got to actually coding it. Anyway, enjoy, and try to ignore the fact that there is code in there that’s not actually being used, or has variables that aren’t created yet. This is a work in progress after all

#-------------------------------------------------------------
# Project:   Toshiko IRC Bot
# Version:   0.5.0-alpha
# Author:    Mark LaDoux
# E-mail:    mark.ladoux@gmail.com
# Website:   http://markladoux.com/
# Copyright: Copyright © 2012, Mark LaDoux. ALL RIGHTS RESERVED
#-------------------------------------------------------------

#----------------------------------------------------------
# include dependencies
#----------------------------------------------------------
require 'socket'
require 'thread'

#-------------------------------------------------------------
# Toshiko Bot Class
#-------------------------------------------------------------

class Toshiko

	#-------------------------------------------------------------
	# Attribute Accessors ( Read and Write )
	#-------------------------------------------------------------
	attr_accessor(
		:nick,  # Use this nick
		)

	#-------------------------------------------------------------
	# Attribute Readers ( Read only, no write access)
	#-------------------------------------------------------------
	attr_reader(
		:version,     # Class version
		:socket,      # Network socket
		:dead_socket, # is the network socket dead?
		)

	#-------------------------------------------------------------
	# Attribute Writers ( Write only, no read access )
	#-------------------------------------------------------------
	attr_writer(
		:server,             # Connect to this server
		:port,               # Use this port
		:server_password,    # Use this password
		:nick_password,      # Identify with this nick
		:real_name,          # Use this real name
		:use_vhost,          # use a vhost
		:verbose,            # return verbose error messages
		)

	#-------------------------------------------------------------
	# Initialize class!
	#-------------------------------------------------------------
	def initialize(opts = {})

		# Library Version
		@version = '0.5.0-alpha'

		# Grab user configurations
		@server          = opts[:server]
		@port            = opts[:port] || 6667
		@server_password = opts[:server_password]
		@nick            = opts[:nick]
		@nick_password   = opts[:nick_password]
		@real_name       = opts[:real_name] || "Toshiko IRCBot #{@version}"
		@use_vhost       = opts[:use_vhost]
		@verbose         = opts[:verbose]

		# get defaults
		defaults
	end

	#-------------------------------------------------------------
	# Defaults
	#-------------------------------------------------------------
	def defaults
		@dead_socket = true
		@reconnect   = true
	end

	#----------------------------------------------------------
	# Create socket
	#
	# Creates a TCP connection to the requested server
	#----------------------------------------------------------
	def create_socket
		# set socket dead to false
		@dead_socket = false

		# create our socket
		begin
			@socket = TCPSocket.new(@server, @port)
		rescue StandardError => e
			@dead_socket = true
			puts "[ERROR] Unable to create connection to #{@server}"
			puts "[ERROR] #{e.inspect}"
			raise
		end
	end

	#----------------------------------------------------------
	# Run the bot
	#----------------------------------------------------------
	def run
		# create a socket
		create_socket

		# ident
		@socket.puts "NICK #{@nick}"
		@socket.puts "USER #{@nick} 0 * :#{@real_name}"

		# start main loop
		main_loop
	end

	#----------------------------------------------------------
	# Main processing loop
	#----------------------------------------------------------
	def main_loop
		until @socket.eof do
			# get our data
			@raw = @socket.gets
			if @verbose
				puts "[RECV] #{@raw}"
			end

			# check for pings
			if @raw.match(/^PING :(.*)/)
				@socket.puts "PONG #{$~[1]}"
				if @verbose
					puts "PONG #{$~[1]}"
				end
				# we're done here, go to next loop iteration
				next
			end

			# we'll do more later, this is just to test our
			# connection before I start implementing the threads
		end
		@dead_socket = true

		run unless @reconnect == false
	end

	#----------------------------------------------------------
	# Missing Method
	#
	# Checks if method is prefixed with a term that the script
	# should ignore, and continues processing gracefully.
	# This is for methods that will vary from bot to bot
	# and won't necessarily all be there. Non-prefixed methods
	# will of course be handled normally, and will cause the
	# bot to crash if they are missing. This is necessary, as
	# if we don't treat them normally, certain tests will act
	# strangly. All elements with values of nil or false, will
	# return true. The call to super stops that from happening
	# and causes data to remain as expected.
	#----------------------------------------------------------
	def method_missing(meth, *args, &block)

		# missing data parser, we don't want to die on this one
		if meth =~ /^process_(.+)$/
			if @verbose
				puts "[WARN] No parsers #{@command} data defined"
			end

		# missing trigger case, we don't want to die on this one either
		elsif meth =~ /^on_(.+)$/
			if @verbose
				puts "[WARN] No triggers for #{@command} event defined"
			end

		# Everything else can go to hell!
		else
			super
		end
	end

end
Flora, IRC

Flora is getting translators!

I’ve managed to recruit some translators for the language files for Flora. So far, I’ve got translators for Dutch and German. I have a possible translator for French and Patois (Jamaican Creole). We’ll see how those come out. I was going to talk about getting translations, but there was only so much to write, so I guess I’ll just give you all status updates.

Authentication system is nearing completion. It’s based on IRC’s ACCESS system. I based it on the IRC system because it’s integrating with IRC, so I figured I’d make it as simple and as flexible as possible for the users of the software. This of course will allow users up to 1000 levels of access ( starting with 0, ending with 999 ). I think that should be plenty of variability for most situations. I’m thinking of adding another layer of ACL on top for the WebUI, but for now, I’m keeping it simple. I’ll be starting work on the plugins system, at which point the bot is going to really start moving forward and actually doing more than just idle. I’m still laying out how I want everything to work, but so far it is looking very promising. We’ll see what the future brings in that department. The Net library is so very near completion that I’m running out of stuff to put into it now. The Parse library is making strides, but is only about 20% complete. It has enough in it to do most things, but it’s missing features that will be required for the authentication system later on down the road still. The main Ircbot controller now detects network loss and channel kicks, and has auto-reconnect and auto-rejoin implemented.

The main features that I need to finish are – User tracking, Authentication, Channel Administration, and Bot Administration. Some of these features won’t be implemented until I finish the plugin system, so stay tuned. This is starting to develop into a very exciting bot!

IRC, Software

IRC Bot in PHP 5.4 Part 4

Started work on the modules system. I know I said I was going to talk about other things, but my head just wasn’t in it. I did code in a couple ctcp responses, as you can see in the repository. I am undecided as to how I want to handle the modules system fully. Anyway, here’s what I have as a rough idea so far.

<?php
/**
 * Module Library
 *
 * Loads and registers modules and capabilities.
 *
 * @author    Mark LaDoux <mark.ladoux@gmail.com>
 * @copyright Copyright © 2012, Mark LaDoux
 * @version   0.1.0
 */

namespace IRC;

class Module
{
	/**
	 * Loaded Modules
	 *
	 * @access protected
	 * @var    array
	 */
	protected $_loaded = array();

	/**
	 * Map of triggers to module functions
	 *
	 * @access protected
	 * @var    array
	 */
	protected $_funcs = array();

	/**
	 * Map of permissions required for triggers
	 *
	 * @access protected
	 * @var    array
	 */
	protected $_perms = array();

	/**
	 * load
	 *
	 * Loads modules, and tells them that this is for IRC use so that the
	 * modules will return the proper function and permission information
	 *
	 * @access public
	 * @param  string
	 * @return void
	 */
	public function load($module)
	{
		$module_name   = "\Module\{$module}";
		$this->$module = new $module_name($access_mode = 'IRC');

		// add module to loaded modules list
		$this->_loaded[$module] = $this->$module->version();

		// get module properties
		$this->_funcs[$module] = $this->$module->get_functions();
		$this->_perms[$module] = $this->$module->get_perms();
	}

	/**
	 * loaded
	 *
	 * Lists loaded modules and versions, or reports whether or not a specific
	 * module is loaded.
	 *
	 * @access public
	 * @param  string            $module  Module name
	 * @param  string            $version Module version
	 * @return array|string|bool
	 */
	public function loaded($module = NULL, $version = NULL)
	{
		// if we aren't looking for a specific module, return a list
		if($module === NULL)
		{
			return $this->_loaded;
		}
		// if we aren't looking for a specific version,
		// return it's version ( can also be used for version compares )
		elseif($version === NULL && $module !== NULL)
		{
			if(isset($this->_loaded[$module]))
			{
				return $this->_loaded[$module];
			}
		}
		// if we require a specific version, return if that version of the
		// module is loaded.
		else
		{
			if(
				isset($this->_loaded[$module]) &&
				$this->_loaded[$module] == $version)
			{
				return TRUE;
			}
		}

		// Module is not loaded, return FALSE
		return FALSE;
	}

	/**
	 * Get module permissions
	 *
	 * @access public
	 * @param  string
	 * @return array|bool
	 */
	public function get_perms($module)
	{
		// if module is loaded, share it's permissions
		if(isset($this->_perms[$module])) return $this->_perms[$module];

		// other wise return FALSE.
		return FALSE;
	}

	/**
	 * Get triggers
	 *
	 * @access public
	 * @param  string
	 * @return array|bool
	 */
	public function get_triggers($module)
	{
		// if module is loaded, return it's triggers
		if(isset($this->_funcs[$module])) return $this->_funcs[$module];

		// otherwise return FALSE
		return FALSE;
	}
}

At this stage it’s not yet linked into the bot. I’ve uploaded it to the repository of course, but I’ve yet to decide if this is how I’m wanting to go about it. I’ll run some tests later. I think I’m already starting to see some major pitfalls to the layout I have so far. Nothing I can’t work my way through, but we’ll see how this all works in the end.