I-Ching Class

I recently updated one of my modules for Drupal 7, and in the process I ended up putting way too much time and energy into the effort by creating a class for it. The module casts the I-Ching, an ancient Chinese system of divination based on elemental energies. This system is very old and I have known about it for years. In 05/2020 I will celebrate my 20th year building websites. I am so proud to have been a minscule part of the amazing indescribable matrix know as the internet.

I wrote my first I-Ching program about 20 years ago at Community College of Vermont, for a C++ programming course I was taking. When I think back to how completely shitty that program was and how stupidly proud I was when it actually ran and produced an I-Ching reading for my teacher… I ended up getting an “A” for that class. I loved writing code and the logic behind it then, and incredibly after all these years, I still feel the same way about it.

There probably has never been a more useless Drupal module. It probably has value as a teaching tool or a novelty for a community site, but I doubt it will ever start a National I-Ching craze. Despite this future annonymity I have very much enjoyed developing it, and I am going to port it to D8 just to punish myself for the next year. (just kidding). The module can be found here: https://www.drupal.org/project/tao_iching. Thanx Interwebz, it’s been well, unforgettable.

class I_Ching {

	function __construct() {

		$this->iching = array("initial" => "", "changed" => "", "question" => "");
		// define the 8 trigrams
		$this->kun = array(0, 0, 0);
		$this->gen = array(1, 0, 0);
		$this->kan = array(0, 1, 0);
		$this->xun = array(1, 1, 0);
		$this->zhen = array(0, 0, 1);
		$this->li = array(1, 0, 1);
		$this->dui = array(0, 1, 1);
		$this->qian = array(1, 1, 1);
	}


	/**
	 * generates i-ching lines
	 */
	public function line() {

		$toss = array();
		$return_vals = array();
		$name = "";
		$code = "";
		$k = 0;
		// toss three coins, get the result (heads = 1, tails = 0)
		for ($k = 0 ; $k < 3; $k++) {
			$toss[] = rand(0, 1);
		}
		// get the name of the line
		switch ($toss) {
			case $toss == $this->kun:
				$name = "kun";
				$code = "000";
				break;
			case $toss == $this->gen:
				$name = "gen";
				$code = "100";
				break;
			case $toss == $this->kan:
				$name = "kan";
				$code = "010";
				break;
			case $toss == $this->xun:
				$name = "xun";
				$code = "110";
				break;
			case $toss == $this->zhen:
				$name = "zhen";
				$code = "001";
				break;
			case $toss == $this->li:
				$name = "li";
				$code = "101";
				break;
			case $toss == $this->dui:
				$name = "dui";
				$code = "011";
				break;
			case $toss == $this->qian:
				$name = "qian";
				$code = "111";
				break;
		}
		// check for changing lines
		if ($toss === $this->qian) {
			$val = 9;
			$line = "yang_changing";
		}
		if ($toss === $this->kun) {
			$val = 6;
			$line = "yin_changing";
		}
		// yin or yang
		$sumtest = array_sum($toss);

		if ($sumtest == 2) {
			$val = 8;
			$line = "yin";
		}
		if ($sumtest == 1) {
			$val = 7;
			$line = "yang";
		}
		$return_vals['line'] = $line;
		$return_vals[$name] = $code;
		$return_vals['coinsval'] = $val;

		return $return_vals;
	}


	/**
	 * generates one complete hexagram
	 */
	public function hexagram() {

		$i = 0;
		$hexagram = array();
		while($i < 6) {

			$hexagram[] = $this->line();
			$i++;		
		}
		return $hexagram;
	}


	/**
	 * generates complete i-ching
	 */
	public function complete($hexagram) {

		$i = 0;
		$changedBucket = array();
		while ($i < 6) {

			$lineValKey = key($hexagram[$i]);
			$lineVal = $hexagram[$i][$lineValKey];

			if ($lineVal == "yang_changing") {

				$changedBucket[$i][$lineValKey] = "yin";

			} else if ($lineVal == "yin_changing") {

				$changedBucket[$i][$lineValKey] = "yang";

			} else {

				$changedBucket[$i][$lineValKey] = $lineVal;
			}
			$i++;
		}
		$this->iching['initial'] = $hexagram;

		foreach ($hexagram as $lineArray) {

			if ( array_key_exists("qian", $lineArray) || array_key_exists("kun", $lineArray) ) {

				$this->iching['changed'] = $changedBucket;
				continue;
			}
		}
		if ( $this->iching['changed'] == "" || empty($this->iching['changed']) ) {
			$this->iching['changed'] = "No Change";
		}
		// $this->iching['initial'][0] is the bottom line
		return $this->iching;
	}


	/**
	 * returns the number of the top changing line
	 */
	public function findTopChanging($rawhex) {

		$lineBucket = array();
		foreach ($rawhex as $key => $value) {

			$lineBucket[$key] = $value['line'];
		}
	   	$yin_keys = array_keys($lineBucket, "yin_changing");
	   	$yang_keys = array_keys($lineBucket, "yang_changing");

	   	$integrated = array_merge($yin_keys, $yang_keys);
	   	sort($integrated, SORT_NUMERIC);

	   	$top_changed = $integrated[0];
	    	switch ($top_changed) {
	     		case "0":
				$line_pos = "six";
				break;
	     		case "1":
				$line_pos = "five";
				break;
	     		case "2":
				$line_pos = "four";
				break;
	     		case "3":
				$line_pos = "three";
				break;
	     		case "4":
				$line_pos = "two";
				break;
	     		case "5":
				$line_pos = "one";
				break;
	  	}
	   	$topchanging = "line_" . $line_pos;
	   	return $topchanging;
	}


	/**
	 * removes "_changing"
	 */
	public function rawhex_cleanup($raw) {

		$cleaned = array();
	   	foreach($raw as $key => $value) {

			switch ($value['line']) {
	     			case "yang_changing":
					$cleaned[$key]['line'] = "yang";
					break;
	     			case "yin_changing":
					$cleaned[$key]['line'] = "yin";
					break;
				default:
					$cleaned[$key]['line'] = $value['line'];
			}
	  	}
	   	return $cleaned;
	}


	/**
	 * makes an ID and a timestamp
	 */
	public function makeID() {

		$time = time();
		$idReturn = array();
		$idReturn['timestamp'] = $time;
		$idReturn['id'] = hash("md5", $time);
		return $idReturn;
	}


	/**
	 * checks if a unix timestamp is older than 1 hour
	 */
	public function checkTimestamp($timeString) {

		if ( !empty($timeString) ) {

			$newTimeString = time();
			$difference = $newTimeString - $timeString;
			$difference = $difference/60; // minutes
			if ($difference >= 60) {

				return TRUE;		
			} else {
				return FALSE; 
			}
		}
	}


	/**
	 * inserts a trigram to the lines table
	 */
	public function insertLine($id, $throw_num, $line, $tri_name, $code, $coinsval) {

		$return = NULL;
		$success = FALSE;
		if ( !empty($id) && !empty($throw_num) && !empty($line) && !empty($tri_name) && !empty($code) && !empty($coinsval) ) {

			if ( $this->readingExist($id) != FALSE ) {

				$return = db_insert('tao_iching_lines')
					->fields(array(
					'id' => $id,
					'throw_num' => $throw_num,
					'line' => $line,
					'tri_name' => $tri_name,
					'code' => $code,
					'coinsval' => $coinsval,
					))
					->execute();
				($return) ? $success = TRUE : $success = FALSE;
			}
		}
		if ($success) {

			return TRUE;
		} else {

			return FALSE;
		}
	}


	/**
	 * checks if a reading exists
	 */
	public function readingExist($id) {

		$ret = FALSE;
		if ( !empty($id) ) {

			$ret = db_query('SELECT id FROM {tao_iching_readings} 
				WHERE id = :id', array(
			  		':id' => $id,
				))->fetchField();
			($ret) ? $ret = TRUE : $ret = FALSE;
		}
		return $ret;
	}


	/**
	 * deletes a reading from the database
	 */
	public function deleteReading($id) {

		$return = FALSE;
		if ( !empty($id) ) {

			$num_lines_deleted = db_delete('tao_iching_lines')
			  ->condition('id', $id)
			  ->execute();
			($num_lines_deleted) ? $linesRet = TRUE : $linesRet = FALSE;

			$num_readings_deleted = db_delete('tao_iching_readings')
			  ->condition('id', $id)
			  ->execute();
			($num_readings_deleted) ? $readsRet = TRUE : $readsRet = FALSE;

			if ($linesRet != FALSE && $readsRet != FALSE) {

				$return = TRUE;
			}
		}
		return $return;
	}


	/**
	 * checks the number of lines in a reading
	 */
	public function checkNumber($id) {

		$ret = "";
		if ( !empty($id) ) {
		
			$count = db_query('SELECT id FROM {tao_iching_lines}
				WHERE id = :id', array(
						':id' => $id,
					))->rowCount();
			($count) ? $ret = $count : $ret = FALSE;
		}
		return $ret;
	}


	/**
	 * initializes a reading
	 */
	public function readingInit($idArray, $question="") {

		if ( !empty($idArray) ) {

			$insert = db_insert('tao_iching_readings')
				->fields(array(
				'id' => $idArray['id'],
				'question' => $question,
				'timestamp' => $idArray['timestamp'],
				))
				->execute();
			return $idArray['timestamp'];
		}
	}


	/**
	 * returns the current I-Ching object
	 */
	public function myIching($id) {

		if ( !empty($id) ) {

			if ( $this->readingExist($id) != FALSE ) {

				// get the readings table info
				$readingArray = db_query('SELECT question, timestamp FROM {tao_iching_readings} 
					WHERE id = :id', array(
						':id' => $id
					))->fetchAssoc();
				// get the trigrams created
				$lines = db_query('SELECT throw_num, line, tri_name, code, coinsval FROM {tao_iching_lines} 
					WHERE id = :id', array(
						':id' => $id
					))->fetchAll();
	
				foreach($lines as $key => $value) {

					$this->iching['initial'][$key]['line'] = $value->line;
					foreach ($value as $arKey => $arVal) {

						if ($arKey != "line" && $arKey != "coinsval") {

							$arKeyBucket = $value->tri_name;
							$arValBucket = $arVal;
						}
					}
					$this->iching['initial'][$key][$arKeyBucket] = $arValBucket;
					$this->iching['initial'][$key]['coinsval'] = $value->coinsval;
				}
				$this->iching['question'] = $readingArray['question'];
			}
		}
		return $this->iching;
	}


	/**
	 * returns the book contents when passed a hexagram array
	 */
	public function findBook($hexagram) {

		$bookNumber = db_query('SELECT book_number FROM {tao_iching_hexagrams} 
			WHERE line1 = :line1 
			AND line2 = :line2 
			AND line3 = :line3 
			AND line4 = :line4 
			AND line5 = :line5 
			AND line6 = :line6', array(
		  		':line1' => $hexagram[0]['line'],
		  		':line2' => $hexagram[1]['line'],
		  		':line3' => $hexagram[2]['line'],
		  		':line4' => $hexagram[3]['line'],
		  		':line5' => $hexagram[4]['line'],
		  		':line6' => $hexagram[5]['line'],
			))->fetchField();

		$bookArray = db_query('SELECT descr, judge, image, line_one, line_two, line_three, line_four, line_five, line_six FROM {tao_iching_books} 
			WHERE number = :number', array(
				':number' => $bookNumber
			))->fetchAssoc();
		return $bookArray;
	}


	/**
	 * returns the book number when passed a hexagram array
	 */
	public function findBooknum($hexagram) {

		$bookNumber = db_query('SELECT book_number FROM {tao_iching_hexagrams} 
			WHERE line1 = :line1 
			AND line2 = :line2 
			AND line3 = :line3 
			AND line4 = :line4 
			AND line5 = :line5 
			AND line6 = :line6', array(
		  		':line1' => $hexagram[0]['line'],
		  		':line2' => $hexagram[1]['line'],
		  		':line3' => $hexagram[2]['line'],
		  		':line4' => $hexagram[3]['line'],
		  		':line5' => $hexagram[4]['line'],
		  		':line6' => $hexagram[5]['line'],
			))->fetchField();
		return $bookNumber;
	}


} // END class I_Ching